diff --git a/.Rbuildignore b/.Rbuildignore index 20d1730c..28d85b5a 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -1,15 +1,16 @@ ^\.git$ ^.*\.Rproj$ ^\.Rproj\.user$ -^README\.md$ ^\.gitignore$ -^\.travis\.yml$ -^TODO$ ^\.Rhistory$ -^bssm.pdf$ -^growth_model.pdf$ ^vignettes/psi_pf_experiments/.*result.*\.rds$ ^vignettes/psi_pf_experiments/.*truth.*\.rds$ ^vignettes/psi_pf_experiments/.*\.sh$ ^vignettes/psi_pf_experiments/.*\.R$ ^\.github$ +^codecov\.yml$ +^codemeta\.json$ +^README\.Rmd +^README_cache$ +^benchmarks$ +^autotest_results\.R$ diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..c50d646f --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# CONTRIBUTING # + +### Fixing typos + +Small typos or grammatical errors in documentation may be edited directly using +the GitHub web interface, so long as the changes are made in the _source_ file. + +* YES: you edit a roxygen comment in a `.R` file below `R/`. +* NO: you edit an `.Rd` file below `man/`. + +### Prerequisites + +Before you make a substantial pull request, you should always file an issue and +make sure someone from the team agrees that it’s a problem. If you’ve found a +bug, create an associated issue and illustrate the bug with a minimal +[reprex](https://www.tidyverse.org/help/#reprex). + +### Pull request process + +* We recommend that you create a Git branch for each pull request (PR). +* Look at the Github Actions build status before and after making changes. +The `README` should contain status badge "R-CMD-check" which acts a link to +Github Actions. +* We recommend the tidyverse [style guide](http://style.tidyverse.org). +You can use the [styler](https://CRAN.R-project.org/package=styler) package to +apply these styles, but please don't restyle code that has nothing to do with +your PR. +* We use [roxygen2](https://cran.r-project.org/package=roxygen2). +* We use [testthat](https://cran.r-project.org/package=testthat). +Contributions with test cases included are easier to accept. +* For user-facing changes, add a bullet to the top of `NEWS.md` below the +current development version header describing the changes made followed by your +GitHub username, and links to relevant issue(s)/PR(s). + +### Thanks for contributing! + +This contributing guide is adapted from the tidyverse contributing guide available at https://raw.githubusercontent.com/r-lib/usethis/master/inst/templates/tidy-contributing.md diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 00000000..836f284c --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,10 @@ + + + + +
Session Info + +```r + +``` +
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..e1beac81 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,20 @@ + + + + + + +## Description + + +## Related Issue + + +## Example + + + diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index a2dbc238..8faf20e0 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -1,14 +1,10 @@ -# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. -# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions +# Workflow derived from https://github.com/r-lib/actions/tree/master/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: - - main - - master + branches: [main, master] pull_request: - branches: - - main - - master + branches: [main, master] name: R-CMD-check @@ -22,62 +18,50 @@ jobs: fail-fast: false matrix: config: + - {os: macOS-latest, r: 'release'} - {os: windows-latest, r: 'release'} - - {os: ubuntu-20.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} - - {os: ubuntu-20.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel/1'} env: - R_REMOTES_NO_ERRORS_FROM_WARNINGS: true - RSPM: ${{ matrix.config.rspm }} GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes steps: - uses: actions/checkout@v2 - - uses: r-lib/actions/setup-r@v1 + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true - - uses: r-lib/actions/setup-pandoc@v1 - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Cache R packages - if: runner.os != 'Windows' - uses: actions/cache@v2 + - uses: r-lib/actions/setup-r-dependencies@v2 with: - path: ${{ env.R_LIBS_USER }} - key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} - restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- - - - name: Install system dependencies - if: runner.os == 'Linux' - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install dependencies - run: | - remotes::install_deps(dependencies = TRUE) - remotes::install_cran("rcmdcheck") - shell: Rscript {0} + extra-packages: rcmdcheck, covr - name: Check env: - _R_CHECK_CRAN_INCOMING_REMOTE_: false - run: rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") + _R_CHECK_CRAN_INCOMING_: false + run: | + options(crayon.enabled = TRUE) + rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") shell: Rscript {0} + - name: Show testthat output + if: always() + run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + - name: Upload check results if: failure() uses: actions/upload-artifact@main with: name: ${{ runner.os }}-r${{ matrix.config.r }}-results path: check + + - name: Test coverage + run: covr::codecov() + shell: Rscript {0} diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml new file mode 100644 index 00000000..98822609 --- /dev/null +++ b/.github/workflows/test-coverage.yaml @@ -0,0 +1,61 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +name: test-coverage.yaml + +permissions: read-all + +jobs: + test-coverage: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::covr, any::xml2 + needs: coverage + + - name: Test coverage + run: | + cov <- covr::package_coverage( + quiet = FALSE, + clean = FALSE, + install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") + ) + covr::to_cobertura(cov) + shell: Rscript {0} + + - uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} + file: ./cobertura.xml + plugin: noop + disable_search: true + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Show testthat output + if: always() + run: | + ## -------------------------------------------------------------------- + find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + + - name: Upload test results + if: failure() + uses: actions/upload-artifact@v4 + with: + name: coverage-test-failures + path: ${{ runner.temp }}/package diff --git a/DESCRIPTION b/DESCRIPTION index cc266d6d..5e7eba75 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,7 @@ Package: bssm Type: Package Title: Bayesian Inference of Non-Linear and Non-Gaussian State Space Models -Version: 1.1.4 -Date: 2021-04-13 +Version: 2.0.3 Authors@R: c(person(given = "Jouni", family = "Helske", @@ -15,24 +14,46 @@ Authors@R: role = "aut", comment = c(ORCID = "0000-0002-8041-7222"))) Description: Efficient methods for Bayesian inference of state space models - via particle Markov chain Monte Carlo (MCMC) and MCMC based on parallel + via Markov chain Monte Carlo (MCMC) based on parallel importance sampling type weighted estimators - (Vihola, Helske, and Franks, 2020, ). + (Vihola, Helske, and Franks, 2020, ), + particle MCMC, and its delayed acceptance version. Gaussian, Poisson, binomial, negative binomial, and Gamma observation densities and basic stochastic volatility models - with linear-Gaussian state dynamics, - as well as general non-linear Gaussian models and discretised - diffusion models are supported. + with linear-Gaussian state dynamics, as well as general non-linear Gaussian + models and discretised diffusion models are supported. + See Helske and Vihola (2021, ) for details. License: GPL (>= 2) -Depends: R (>= 3.5.0) -Suggests: dplyr, ggplot2 (>= 2.0.0), Hmisc, KFAS (>= 1.2.1), knitr (>= - 1.11), MASS, ramcmc, rmarkdown (>= 0.8.1), sde, sitmo, testthat -Imports: coda (>= 0.18-1), diagis, Rcpp (>= 0.12.3) -LinkingTo: Rcpp, RcppArmadillo, ramcmc, sitmo -SystemRequirements: C++11, pandoc (>= 1.12.3, needed for vignettes) -RoxygenNote: 7.1.1 +Depends: R (>= 4.1.0) +Suggests: + covr, + ggplot2 (>= 2.0.0), + KFAS (>= 1.2.1), + knitr (>= 1.11), + MASS, + rmarkdown (>= 0.8.1), + ramcmc, + sde, + sitmo, + testthat +Imports: + bayesplot, + checkmate, + coda (>= 0.18-1), + diagis, + dplyr, + posterior, + Rcpp (>= 0.12.3), + rlang, + tidyr +LinkingTo: ramcmc, Rcpp, RcppArmadillo, sitmo +SystemRequirements: pandoc (>= 1.12.3, needed for vignettes) VignetteBuilder: knitr BugReports: https://github.com/helske/bssm/issues +URL: https://github.com/helske/bssm ByteCompile: true Encoding: UTF-8 NeedsCompilation: yes +RoxygenNote: 7.2.3 +Roxygen: list(markdown = TRUE, + roclets = c("namespace", "rd", "srr::srr_stats_roclet")) diff --git a/NAMESPACE b/NAMESPACE index b516bf6d..d8021a1c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,93 +1,131 @@ -# Generated by roxygen2: do not edit by hand - -S3method(as.data.frame,mcmc_output) -S3method(bootstrap_filter,gaussian) -S3method(bootstrap_filter,nongaussian) -S3method(bootstrap_filter,ssm_nlg) -S3method(bootstrap_filter,ssm_sde) -S3method(ekpf_filter,ssm_nlg) -S3method(fast_smoother,gaussian) -S3method(fast_smoother,nongaussian) -S3method(gaussian_approx,nongaussian) -S3method(gaussian_approx,ssm_nlg) -S3method(importance_sample,nongaussian) -S3method(kfilter,gaussian) -S3method(kfilter,nongaussian) -S3method(logLik,gaussian) -S3method(logLik,nongaussian) -S3method(logLik,ssm_nlg) -S3method(logLik,ssm_sde) -S3method(particle_smoother,gaussian) -S3method(particle_smoother,nongaussian) -S3method(particle_smoother,ssm_nlg) -S3method(particle_smoother,ssm_sde) -S3method(predict,mcmc_output) -S3method(print,mcmc_output) -S3method(run_mcmc,gaussian) -S3method(run_mcmc,nongaussian) -S3method(run_mcmc,ssm_nlg) -S3method(run_mcmc,ssm_sde) -S3method(sim_smoother,gaussian) -S3method(sim_smoother,nongaussian) -S3method(smoother,gaussian) -S3method(smoother,nongaussian) -S3method(summary,mcmc_output) -export(ar1_lg) -export(ar1_ng) -export(as_bssm) -export(asymptotic_var) -export(bootstrap_filter) -export(bsm_lg) -export(bsm_ng) -export(ekf) -export(ekf_smoother) -export(ekpf_filter) -export(expand_sample) -export(fast_smoother) -export(gamma) -export(gaussian_approx) -export(halfnormal) -export(importance_sample) -export(kfilter) -export(normal) -export(particle_smoother) -export(post_correct) -export(run_mcmc) -export(sim_smoother) -export(smoother) -export(ssm_mlg) -export(ssm_mng) -export(ssm_nlg) -export(ssm_sde) -export(ssm_ulg) -export(ssm_ung) -export(suggest_N) -export(svm) -export(tnormal) -export(ukf) -export(uniform) -importFrom(Rcpp,evalCpp) -importFrom(coda,mcmc) -importFrom(coda,spectrum0.ar) -importFrom(diagis,ess) -importFrom(diagis,weighted_mean) -importFrom(diagis,weighted_se) -importFrom(diagis,weighted_var) -importFrom(stats,"tsp<-") -importFrom(stats,as.ts) -importFrom(stats,cov) -importFrom(stats,dnorm) -importFrom(stats,end) -importFrom(stats,frequency) -importFrom(stats,is.ts) -importFrom(stats,logLik) -importFrom(stats,qlogis) -importFrom(stats,quantile) -importFrom(stats,sd) -importFrom(stats,start) -importFrom(stats,time) -importFrom(stats,ts) -importFrom(stats,ts.union) -importFrom(stats,tsp) -importFrom(stats,var) -useDynLib(bssm) +# Generated by roxygen2: do not edit by hand + +S3method(as.data.frame,mcmc_output) +S3method(as_draws,mcmc_output) +S3method(as_draws_df,mcmc_output) +S3method(bootstrap_filter,lineargaussian) +S3method(bootstrap_filter,nongaussian) +S3method(bootstrap_filter,ssm_nlg) +S3method(bootstrap_filter,ssm_sde) +S3method(ekpf_filter,ssm_nlg) +S3method(fast_smoother,lineargaussian) +S3method(fast_smoother,nongaussian) +S3method(fitted,mcmc_output) +S3method(gaussian_approx,nongaussian) +S3method(gaussian_approx,ssm_nlg) +S3method(importance_sample,nongaussian) +S3method(kfilter,lineargaussian) +S3method(kfilter,nongaussian) +S3method(logLik,lineargaussian) +S3method(logLik,nongaussian) +S3method(logLik,ssm_nlg) +S3method(logLik,ssm_sde) +S3method(particle_smoother,lineargaussian) +S3method(particle_smoother,nongaussian) +S3method(particle_smoother,ssm_nlg) +S3method(particle_smoother,ssm_sde) +S3method(plot,mcmc_output) +S3method(predict,mcmc_output) +S3method(print,mcmc_output) +S3method(run_mcmc,lineargaussian) +S3method(run_mcmc,nongaussian) +S3method(run_mcmc,ssm_nlg) +S3method(run_mcmc,ssm_sde) +S3method(sim_smoother,lineargaussian) +S3method(sim_smoother,nongaussian) +S3method(smoother,lineargaussian) +S3method(smoother,nongaussian) +S3method(summary,mcmc_output) +export(ar1_lg) +export(ar1_ng) +export(as_bssm) +export(as_draws) +export(as_draws_df) +export(asymptotic_var) +export(bootstrap_filter) +export(bsm_lg) +export(bsm_ng) +export(check_diagnostics) +export(cpp_example_model) +export(ekf) +export(ekf_fast_smoother) +export(ekf_smoother) +export(ekpf_filter) +export(estimate_ess) +export(expand_sample) +export(fast_smoother) +export(gamma) +export(gamma_prior) +export(gaussian_approx) +export(halfnormal) +export(halfnormal_prior) +export(iact) +export(importance_sample) +export(kfilter) +export(normal) +export(normal_prior) +export(particle_smoother) +export(post_correct) +export(run_mcmc) +export(sim_smoother) +export(smoother) +export(ssm_mlg) +export(ssm_mng) +export(ssm_nlg) +export(ssm_sde) +export(ssm_ulg) +export(ssm_ung) +export(suggest_N) +export(svm) +export(tnormal) +export(tnormal_prior) +export(ukf) +export(uniform) +export(uniform_prior) +importFrom(Rcpp,evalCpp) +importFrom(bayesplot,mcmc_combo) +importFrom(checkmate,test_atomic_vector) +importFrom(checkmate,test_count) +importFrom(checkmate,test_double) +importFrom(checkmate,test_flag) +importFrom(checkmate,test_int) +importFrom(checkmate,test_integerish) +importFrom(checkmate,test_numeric) +importFrom(coda,mcmc) +importFrom(diagis,ess) +importFrom(diagis,weighted_mean) +importFrom(diagis,weighted_quantile) +importFrom(diagis,weighted_se) +importFrom(diagis,weighted_var) +importFrom(dplyr,as_tibble) +importFrom(dplyr,group_by) +importFrom(dplyr,summarise) +importFrom(dplyr,ungroup) +importFrom(posterior,as_draws) +importFrom(posterior,as_draws_df) +importFrom(posterior,default_convergence_measures) +importFrom(posterior,ess_mean) +importFrom(posterior,summarise_draws) +importFrom(rlang,.data) +importFrom(rlang,is_interactive) +importFrom(stats,"tsp<-") +importFrom(stats,as.ts) +importFrom(stats,cov) +importFrom(stats,dnorm) +importFrom(stats,end) +importFrom(stats,fitted) +importFrom(stats,frequency) +importFrom(stats,is.ts) +importFrom(stats,logLik) +importFrom(stats,na.omit) +importFrom(stats,qlogis) +importFrom(stats,quantile) +importFrom(stats,sd) +importFrom(stats,start) +importFrom(stats,time) +importFrom(stats,ts) +importFrom(stats,ts.union) +importFrom(stats,tsp) +importFrom(stats,var) +importFrom(tidyr,pivot_wider) +useDynLib(bssm) diff --git a/NEWS b/NEWS.md similarity index 51% rename from NEWS rename to NEWS.md index 1f4be274..a34483ff 100644 --- a/NEWS +++ b/NEWS.md @@ -1,196 +1,315 @@ -bssm 1.1.4 (Release date: 2021-03-) +bssm 2.0.3 (Release date: 2025-09-24) +===================================== + * Syntax changes in C++ for finite value checks due to changes in Armadillo. + +bssm 2.0.2 (Release date: 2023-10-18) +===================================== + * Switched to markdown NEWS with a plan to be more clear about the future + changes in the package. + * Added more details to the `?bssm` help page. + * Added more details to the `?bssm_prior` help page. + * Added option to extract only hyperparameters in `as_draws` method. Also + fixed a bug in `as_draws` which caused the it to ignore `states` argument. + * Added a default plot method for the `run_mcmc` output. + * Fixed the aliases of the main help page to accomodate changes in roxygen2. + * Removed explicit C++ version requirement as required by new CRAN policies. + * Removed `magrittr` dependency and switched to native pipe, leading to + requirement for R 4.1.0+. + * Added Sys.setenv("OMP_NUM_THREADS" = 2) to (partially) fix CRAN issues with + parallelisation on Debian. + +bssm 2.0.1 (Release date: 2022-05-02) ============== - * Better documentation for SV model, and changed ordering of arguments to emphasise the - recommended parameterization. + * Fixed weights to one in case of non-linear model with mcmc_type="approx". + * Adjusted tolerance of some testthat tests to comply with CRAN's MKL checks. + +bssm 2.0.0 (Release date: 2021-11-26) +============== + * Added a progress bar for run_mcmc. + * Added a fitted method for extraction of summary statistics of posterior + predictive distribution p(y_t | y_1, ..., y_n) for t = 1, ..., n. + * Rewrote the summary method completely, which now returns data.frame. This + also resulted in some changes in order of the function arguments. + * The output of predict method is now a data frame with column weight + corresponding to the IS-weights in case of IS-MCMC. Previously resampling + was done internally, but now this is left for the user if needed + (i.e. for drawing state trajectories). + * The asymptotic_var and iact functions are now exported to users, and they + also contain alternative methods based on the posterior package. + * New function estimate_ess can be used to compute effective sample size + from weighted MCMC. + * Added compatibility with the posterior package by defining as_draws + method for converting run_mcmc output to draws_df object. + * New function check_diagnostics for quick glance of ESS and Rhat values. + * Large number of new tests, and improved documentation with added examples. + * Large number of internal tweaks so that the package complies with + goodpractices package and Ropensci statistical software standards. + +bssm 1.1.7-1 (Release date: 2021-09-21) +============== + * Fixed an error in automatic tests due to lack of fixed RNG seed. + +bssm 1.1.7 (Release date: 2021-09-20) +============== + * Added a function cpp_example_model which can be used to extract and + compile some non-linear and SDE models used in the examples and vignettes. + * Added as_draws method for run_mcmc output so samples can be analysed using + the posterior package. + * Added more examples. + * Fixed a tolerance of one MCMC test to pass the test on OSX as well. + * Fixed a bug in iterated extended Kalman smoothing which resulted incorrect + estimates. + +bssm 1.1.6 (Release date: 2021-09-06) +============== + * Cleaned some codes and added lots of tests in line with pkgcheck tests. + * Fixed a bug in EKF-based particle filter which returned filtered estimates + also in place of one-step ahead predictions. + * Fixed a bug which caused an error in suggest_N for nlg_ssm. + * Fixed a bug which caused incorrect sampling of smoothing distribution for + ar1_lg model when predicting past or when using simulation smoother. + * Fixed a bug which caused an error when predicting past values in + multivariate time series case. + * Fixed log-likelihood computation for gamma model with non-constant shape + parameter when using (intermediate) Gaussian approximation. + * Fixed sampling of negative binomial distribution in predict method, which + used std::negative_binomial which converts non-integer phi to integer. + Sampling now uses Gamma-Poisson mixture for simulation. + + +bssm 1.1.5 (Release date: 2021-06-14) +============== + * Added explicit check for nsim > 0 in predict method as sample function + works with missing argument causing crypting warnings later. + * Updated drownings data until 2019 and changed the temperature variable + to an average over three stations. + * Improved checks for observations and distributions in model building. + +bssm 1.1.4 (Release date: 2021-04-13) +============== + * Better documentation for SV model, and changed ordering of arguments to + emphasise the recommended parameterization. * Fixed predict method for SV model. - * Removed parallelization in one example which failed on Solaris for some unknown reason. - + * Removed parallelization in one example which failed on Solaris for some + unknown reason. + bssm 1.1.3-2 (Release date: 2021-02-24) ============== - * Fixed missing parenthesis causing compilation fail in case of no OpenMP support. + * Fixed missing parenthesis causing compilation fail in case of no OpenMP + support. * Added pandoc version >= 1.12.3 to system requirements. * Restructured C++ classes so no R structures are present in OpenMP regions. bssm 1.1.3-1 (Release date: 2021-02-22) ============== * Fixed PM-MCMC and DA-MCMC for SDE models and added an example to `ssm_sde`. - * Fixed the state covariance estimates of IS-MCMC, approx-MCMC, and + * Fixed the state covariance estimates of IS-MCMC, approx-MCMC, and Gaussian MCMC when output_type = "summary". - * Fixed memory leaks due to uninitialized variables due to aborted particle filter. - * Fixed numerical issues of multivariate normal density for nonlinear models. + * Fixed memory leaks due to uninitialized variables due to aborted particle + filter. + * Fixed numerical issues of multivariate normal density for nonlinear + models. * Removed dependency on R::lchoose for safer parallel code. * Added vignette for SDE models. * Updated citation information and streamlined the main vignette. - + bssm 1.1.2 (Release date: 2021-02-08) ============== - * Changed the definition of D in ssm_ulg and ssm_ung, functions now accept D as scalar or vector as + * Changed the definition of D in ssm_ulg and ssm_ung, functions now accept + D as scalar or vector as was originally intended. - * Fixed a segfault issue with parallel state sampling in general ssm_ulg/mlg/ung/mng models - caused by calls to R function inside parallel region. - * Fixed a bug from version 1.0.0 in IS1 type sampling which actually lead to IS2 type sampling. + * Fixed a segfault issue with parallel state sampling in general + ssm_ulg/mlg/ung/mng models caused by calls to R function inside parallel + region. + * Fixed a bug from version 1.0.0 in IS1 type sampling which actually lead + to IS2 type sampling. * Fixed out-of-bounds error in IS3 sampling. - * Fixed weight computations for multivariate nonlinear models in case of psi-APF - in some border cases with non-standard H. + * Fixed weight computations for multivariate nonlinear models in case of + psi-APF in some border cases with non-standard H. * Removed Armadillo bound checks for efficiency gains. - + bssm 1.1.1 (Release date: 2021-01-22) ============== - - * Added missing scaling for Gamma distribution in importance sampling weights for added numerical robustness. + + * Added missing scaling for Gamma distribution in importance sampling + weights for added numerical robustness. * Fixed sequential importance sampling for multivariate non-gaussian models. * Fixed simulation smoother for multivariate Gaussian models. bssm 1.1.0 (Release date: 2021-01-19) ============== - - * Added function `suggest_N` which can be used to choose + + * Added function `suggest_N` which can be used to choose suitable number of particles for IS-MCMC. - * Added function `post_correct` which can be used to update + * Added function `post_correct` which can be used to update previous approximate MCMC with IS-weights. - * Gamma priors are now supported in easy-to-use models such as `bsm_lg`. - * The adaptation of the proposal distribution now continues also after the burn-in by default. + * Gamma priors are now supported in easy-to-use models such as `bsm_lg`. + * The adaptation of the proposal distribution now continues also after the + burn-in by default. * Changed default MCMC type to typically most efficient and robust IS2. - * Renamed `nsim` argument to `particles` in most of the R functions (`nsim` also works with a warning). - * Fixed a bug with bsm models with covariates, where all standard deviation parameters were fixed. - This resulted error within MCMC algorithms. - * Fixed a dimension drop bug in the predict method which caused error for univariate models. + * Renamed `nsim` argument to `particles` in most of the R functions (`nsim` + also works with a warning). + * Fixed a bug with bsm models with covariates, where all standard deviation + parameters were fixed. This resulted error within MCMC algorithms. + * Fixed a dimension drop bug in the predict method which caused error for + univariate models. * Fixed some docs and added more examples. * Fixed few typos in vignette (thanks Kyle Hussman) * Reduced runtime of MCMC in growth model vignette as requested by CRAN. - + bssm 1.0.1-1 (Release date: 2020-11-12) ============== - * Added an argument `future` for predict method which allows - predictions for current time points by supplying the original model - (e.g., for posterior predictive checks). + * Added an argument `future` for predict method which allows + predictions for current time points by supplying the original model + (e.g., for posterior predictive checks). At the same time the argument name `future_model` was changed to `model`. - * Fixed a bug in summary.mcmc_run which resulted error when + * Fixed a bug in summary.mcmc_run which resulted error when trying to obtain summary for states only. - * Added a check for Kalman filter for a degenerate case where all + * Added a check for Kalman filter for a degenerate case where all observational level and state level variances are zero. - * Renamed argument `n_threads` to `threads` for consistency + * Renamed argument `n_threads` to `threads` for consistency with `iter` and `burnin` arguments. * Improved documentation, added examples. * Added a vignette regarding psi-APF for non-linear models. - + bssm 1.0.0 (Release date: 2020-06-09) ============== Major update - * Major changes for model definitions, now model updating and priors - can be defined via R functions (non-linear and SDE models still rely on C++ snippets). + * Major changes for model definitions, now model updating and priors + can be defined via R functions (non-linear and SDE models still rely on + C++ snippets). * Added support for multivariate non-Gaussian models. * Added support for gamma distributions. - * Added the function as.data.frame for mcmc output which converts the MCMC samples - to data.frame format for easier post-processing. + * Added the function as.data.frame for mcmc output which converts the MCMC + samples to data.frame format for easier post-processing. * Added truncated normal prior. - * Many argument names and model building functions have been changed for clarity and consistency. - * Major overhaul of C++ internals which can bring minor efficiency gains and smaller installation size. - * Allow zero as initial value for positive-constrained parameters of bsm models. - * Small changes to summary method which can now return also only summaries of the states. - * Fixed a bug in initializing run_mcmc for negative binomial model. + * Many argument names and model building functions have been changed for + clarity and consistency. + * Major overhaul of C++ internals which can bring minor efficiency gains + and smaller installation size. + * Allow zero as initial value for positive-constrained parameters of bsm + models. + * Small changes to summary method which can now return also only summaries + of the states. + * Fixed a bug in initializing run_mcmc for negative binomial model. * Fixed a bug in phi-APF for non-linear models. - * Reimplemented predict method which now always produces data frame of samples. - + * Reimplemented predict method which now always produces data frame of + samples. + bssm 0.1.11 (Release date: 2020-02-25) ============== - * Switched (back) to approximate posterior in RAM for PM-SPDK and PM-PSI, + * Switched (back) to approximate posterior in RAM for PM-SPDK and PM-PSI, as it seems to work better with noisy likelihood estimates. * Print and summary methods for MCMC output are now coherent in their output. - + bssm 0.1.10 (Release date: 2020-02-04) ============== * Fixed missing weight update for IS-SPDK without OPENMP flag. * Removed unused usage argument ... from expand_sample. - + bssm 0.1.9 (Release date: 2020-01-27) ============== * Fixed state sampling for PM-MCMC with SPDK. * Added ts attribute for svm model. * Corrected asymptotic variance for summary methods. - + bssm 0.1.8-1 (Release date: 2019-12-20) ============== * Tweaked tests in order to pass MKL case at CRAN. bssm 0.1.8 (Release date: 2019-09-23) ============== - * Fixed a bug in predict method which prevented the method working in case of ngssm models. - * Fixed a bug in predict method which threw an error due to dimension drop of models with single state. + * Fixed a bug in predict method which prevented the method working in case + of ngssm models. + * Fixed a bug in predict method which threw an error due to dimension drop of + models with single state. * Fixed issues with the vignette. bssm 0.1.7 (Release date: 2019-03-19) ============== - * Fixed a bug in EKF smoother which resulted wrong smoothed state estimates in - case of partially missing multivariate observations. Thanks for Santeri Karppinen for spotting the bug. - * Added twisted SMC based simulation smoothing algorithm for Gaussian models, as an alternative to - Kalman smoother based simulation. - + * Fixed a bug in EKF smoother which resulted wrong smoothed state estimates + in case of partially missing multivariate observations. Thanks for Santeri + Karppinen for spotting the bug. + * Added twisted SMC based simulation smoothing algorithm for Gaussian models, + as an alternative to Kalman smoother based simulation. + bssm 0.1.6-1 (Release date: 2018-11-20) ============== - * Fixed wrong dimension declarations in pseudo-marginal MCMC and logLik methods for SDE and ng_ar1 models. + * Fixed wrong dimension declarations in pseudo-marginal MCMC and logLik + methods for SDE and ng_ar1 models. * Added a missing Jacobian for ng_bsm and bsm models using IS-correction. - * Changed internal parameterization of ng_bsm and bsm models from log(1+theta) to log(theta). - + * Changed internal parameterization of ng_bsm and bsm models from + log(1+theta) to log(theta). + bssm 0.1.5 (Release date: 2018-05-23) ============== - * Fixed the Cholesky decomposition in filtering recursions of multivariate models. + * Fixed the Cholesky decomposition in filtering recursions of multivariate + models. * as_gssm now works for multivariate Gaussian models of KFAS as well. - * Fixed several issues regarding partially missing observations in multivariate models. + * Fixed several issues regarding partially missing observations in + multivariate models. * Added the MASS package to Suggests as it is used in some unit tests. * Added missing type argument to SDE MCMC call with delayed acceptance. - + bssm 0.1.4-1 (Release date: 2018-02-04) ============== * Fixed the use of uninitialized values in psi-filter from version 0.1.3. bssm 0.1.4 (Release date: 2018-02-04) ============== - * MCMC output can now be defined with argument `type`. Instead of returning joint posterior - samples, run_mcmc can now return only marginal samples of theta, or summary statistics of - the states. - * Due to the above change, argument `sim_states` was removed from the Gaussian MCMC methods. - * MCMC functions are now less memory intensive, especially with `type="theta"`. + * MCMC output can now be defined with argument `type`. Instead of returning + joint posterior samples, run_mcmc can now return only marginal samples of + theta, or summary statistics of the states. + * Due to the above change, argument `sim_states` was removed from the + Gaussian MCMC methods. + * MCMC functions are now less memory intensive, especially with + `type="theta"`. bssm 0.1.3 (Release date: 2018-01-07) ============== * Streamlined the output of the print method for MCMC results. - * Fixed major bugs in predict method which caused wrong values for the prediction intervals. + * Fixed major bugs in predict method which caused wrong values for the + prediction intervals. * Fixed some package dependencies. - * Sampling for standard deviation parameters of BSM and their non-Gaussian counterparts - is now done in logarithmic scale for slightly increased efficiency. - * Added a new model class ar1 for univariate (possibly noisy) Gaussian AR(1) processes. - * MCMC output now includes posterior predictive distribution of states for one step ahead - to the future. - + * Sampling for standard deviation parameters of BSM and their non-Gaussian + counterparts is now done in logarithmic scale for slightly increased + efficiency. + * Added a new model class ar1 for univariate (possibly noisy) Gaussian AR(1) + processes. + * MCMC output now includes posterior predictive distribution of states for + one step ahead to the future. + bssm 0.1.2 (Release date: 2017-11-21) ============== - * API change for run_mcmc: All MCMC methods are now under the argument method, - instead of having separate arguments for delayed acceptance and IS schemes. - * summary method for MCMC output now omits the computation of SE and ESS in order - to speed up the function. - * Added new model class lgg_ssm, which is a linear-Gaussian model defined + * API change for run_mcmc: All MCMC methods are now under the argument + method, instead of having separate arguments for delayed acceptance and IS + schemes. + * summary method for MCMC output now omits the computation of SE and ESS in + order to speed up the function. + * Added new model class lgg_ssm, which is a linear-Gaussian model defined directly via C++ like non-linear ssm_nlg models. This allows more flexible prior definitions and complex system matrix constructions. - * Added another new model class, ssm_sde, which is a model with continuous - state dynamics defined as SDE. These too are defined via couple + * Added another new model class, ssm_sde, which is a model with continuous + state dynamics defined as SDE. These too are defined via couple simple C++ functions. * Added non-gaussian AR(1) model class. - * Added argument nsim for predict method, which allows multiple draws per MCMC iteration. - * The noise multiplier matrices H and R in ssm_nlg models can now depend on states. - + * Added argument nsim for predict method, which allows multiple draws per + MCMC iteration. + * The noise multiplier matrices H and R in ssm_nlg models can now depend on + states. + bssm 0.1.1-1 (Release date: 2017-06-27) ============== * Use byte compiler. * Skip tests relying in certain numerical precision on CRAN. - + bssm 0.1.1 (Release date: 2017-06-27) ============== - + * Switched from C++11 PRNGs to sitmo. * Fixed some portability issues in C++ codes. diff --git a/R/RcppExports.R b/R/RcppExports.R index 176f97a5..77cdc613 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -1,227 +1,231 @@ -# Generated by using Rcpp::compileAttributes() -> do not edit by hand -# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 - -gaussian_approx_model <- function(model_, model_type) { - .Call('_bssm_gaussian_approx_model', PACKAGE = 'bssm', model_, model_type) -} - -gaussian_approx_model_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, max_iter, conv_tol, iekf_iter) { - .Call('_bssm_gaussian_approx_model_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, max_iter, conv_tol, iekf_iter) -} - -bsf <- function(model_, nsim, seed, gaussian, model_type) { - .Call('_bssm_bsf', PACKAGE = 'bssm', model_, nsim, seed, gaussian, model_type) -} - -bsf_smoother <- function(model_, nsim, seed, gaussian, model_type) { - .Call('_bssm_bsf_smoother', PACKAGE = 'bssm', model_, nsim, seed, gaussian, model_type) -} - -bsf_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) { - .Call('_bssm_bsf_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) -} - -bsf_smoother_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) { - .Call('_bssm_bsf_smoother_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) -} - -ekf_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) { - .Call('_bssm_ekf_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) -} - -ekf_smoother_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) { - .Call('_bssm_ekf_smoother_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) -} - -ekf_fast_smoother_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) { - .Call('_bssm_ekf_fast_smoother_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) -} - -ekpf <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) { - .Call('_bssm_ekpf', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) -} - -ekpf_smoother <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) { - .Call('_bssm_ekpf_smoother', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) -} - -importance_sample_ng <- function(model_, nsim, use_antithetic, seed, model_type) { - .Call('_bssm_importance_sample_ng', PACKAGE = 'bssm', model_, nsim, use_antithetic, seed, model_type) -} - -gaussian_kfilter <- function(model_, model_type) { - .Call('_bssm_gaussian_kfilter', PACKAGE = 'bssm', model_, model_type) -} - -gaussian_loglik <- function(model_, model_type) { - .Call('_bssm_gaussian_loglik', PACKAGE = 'bssm', model_, model_type) -} - -nongaussian_loglik <- function(model_, nsim, sampling_method, seed, model_type) { - .Call('_bssm_nongaussian_loglik', PACKAGE = 'bssm', model_, nsim, sampling_method, seed, model_type) -} - -nonlinear_loglik <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter, method) { - .Call('_bssm_nonlinear_loglik', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter, method) -} - -gaussian_mcmc <- function(model_, output_type, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, model_type) { - .Call('_bssm_gaussian_mcmc', PACKAGE = 'bssm', model_, output_type, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, model_type) -} - -nongaussian_pm_mcmc <- function(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type) { - .Call('_bssm_nongaussian_pm_mcmc', PACKAGE = 'bssm', model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type) -} - -nongaussian_da_mcmc <- function(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type) { - .Call('_bssm_nongaussian_da_mcmc', PACKAGE = 'bssm', model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type) -} - -nongaussian_is_mcmc <- function(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, is_type, model_type, approx) { - .Call('_bssm_nongaussian_is_mcmc', PACKAGE = 'bssm', model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, is_type, model_type, approx) -} - -nonlinear_pm_mcmc <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type) { - .Call('_bssm_nonlinear_pm_mcmc', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type) -} - -nonlinear_da_mcmc <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type) { - .Call('_bssm_nonlinear_da_mcmc', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type) -} - -nonlinear_ekf_mcmc <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, iekf_iter, output_type) { - .Call('_bssm_nonlinear_ekf_mcmc', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, iekf_iter, output_type) -} - -nonlinear_is_mcmc <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, is_type, sampling_method, max_iter, conv_tol, iekf_iter, output_type, approx) { - .Call('_bssm_nonlinear_is_mcmc', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, is_type, sampling_method, max_iter, conv_tol, iekf_iter, output_type, approx) -} - -R_milstein <- function(x0, L, t, theta, drift_pntr, diffusion_pntr, ddiffusion_pntr, positive, seed) { - .Call('_bssm_R_milstein', PACKAGE = 'bssm', x0, L, t, theta, drift_pntr, diffusion_pntr, ddiffusion_pntr, positive, seed) -} - -suggest_n_nongaussian <- function(model_, theta, candidates, replications, seed, model_type) { - .Call('_bssm_suggest_n_nongaussian', PACKAGE = 'bssm', model_, theta, candidates, replications, seed, model_type) -} - -suggest_n_nonlinear <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, theta_map, candidates, replications, seed) { - .Call('_bssm_suggest_n_nonlinear', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, theta_map, candidates, replications, seed) -} - -postcorrection_nongaussian <- function(model_, model_type, output_type, nsim, seed, n_threads, is_type, counts, theta, modes) { - .Call('_bssm_postcorrection_nongaussian', PACKAGE = 'bssm', model_, model_type, output_type, nsim, seed, n_threads, is_type, counts, theta, modes) -} - -postcorrection_nonlinear <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta_init, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, output_type, nsim, seed, n_threads, is_type, counts, theta, modes) { - .Call('_bssm_postcorrection_nonlinear', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta_init, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, output_type, nsim, seed, n_threads, is_type, counts, theta, modes) -} - -gaussian_predict <- function(model_, theta, alpha, predict_type, seed, model_type) { - .Call('_bssm_gaussian_predict', PACKAGE = 'bssm', model_, theta, alpha, predict_type, seed, model_type) -} - -nongaussian_predict <- function(model_, theta, alpha, predict_type, seed, model_type) { - .Call('_bssm_nongaussian_predict', PACKAGE = 'bssm', model_, theta, alpha, predict_type, seed, model_type) -} - -nonlinear_predict <- function(y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed) { - .Call('_bssm_nonlinear_predict', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed) -} - -gaussian_predict_past <- function(model_, theta, alpha, predict_type, seed, model_type) { - .Call('_bssm_gaussian_predict_past', PACKAGE = 'bssm', model_, theta, alpha, predict_type, seed, model_type) -} - -nongaussian_predict_past <- function(model_, theta, alpha, predict_type, seed, model_type) { - .Call('_bssm_nongaussian_predict_past', PACKAGE = 'bssm', model_, theta, alpha, predict_type, seed, model_type) -} - -nonlinear_predict_past <- function(y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed) { - .Call('_bssm_nonlinear_predict_past', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed) -} - -gaussian_psi_smoother <- function(model_, nsim, seed, model_type) { - .Call('_bssm_gaussian_psi_smoother', PACKAGE = 'bssm', model_, nsim, seed, model_type) -} - -psi_smoother <- function(model_, nsim, seed, model_type) { - .Call('_bssm_psi_smoother', PACKAGE = 'bssm', model_, nsim, seed, model_type) -} - -psi_smoother_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter) { - .Call('_bssm_psi_smoother_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter) -} - -loglik_sde <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) { - .Call('_bssm_loglik_sde', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) -} - -bsf_sde <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) { - .Call('_bssm_bsf_sde', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) -} - -bsf_smoother_sde <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) { - .Call('_bssm_bsf_smoother_sde', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) -} - -sde_pm_mcmc <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type) { - .Call('_bssm_sde_pm_mcmc', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type) -} - -sde_da_mcmc <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type) { - .Call('_bssm_sde_da_mcmc', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type) -} - -sde_is_mcmc <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, is_type, n_threads, type) { - .Call('_bssm_sde_is_mcmc', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, is_type, n_threads, type) -} - -sde_state_sampler_bsf_is2 <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, nsim, L_f, seed, approx_loglik_storage, theta) { - .Call('_bssm_sde_state_sampler_bsf_is2', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, nsim, L_f, seed, approx_loglik_storage, theta) -} - -gaussian_smoother <- function(model_, model_type) { - .Call('_bssm_gaussian_smoother', PACKAGE = 'bssm', model_, model_type) -} - -gaussian_ccov_smoother <- function(model_, model_type) { - .Call('_bssm_gaussian_ccov_smoother', PACKAGE = 'bssm', model_, model_type) -} - -gaussian_fast_smoother <- function(model_, model_type) { - .Call('_bssm_gaussian_fast_smoother', PACKAGE = 'bssm', model_, model_type) -} - -gaussian_sim_smoother <- function(model_, nsim, use_antithetic, seed, model_type) { - .Call('_bssm_gaussian_sim_smoother', PACKAGE = 'bssm', model_, nsim, use_antithetic, seed, model_type) -} - -ukf_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, alpha, beta, kappa) { - .Call('_bssm_ukf_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, alpha, beta, kappa) -} - -conditional_cov <- function(Vt, Ct, use_svd) { - invisible(.Call('_bssm_conditional_cov', PACKAGE = 'bssm', Vt, Ct, use_svd)) -} - -dmvnorm <- function(x, mean, sigma, lwr, logd) { - .Call('_bssm_dmvnorm', PACKAGE = 'bssm', x, mean, sigma, lwr, logd) -} - -precompute_dmvnorm <- function(sigma, Linv, nonzero) { - .Call('_bssm_precompute_dmvnorm', PACKAGE = 'bssm', sigma, Linv, nonzero) -} - -fast_dmvnorm <- function(x, mean, Linv, nonzero, constant) { - .Call('_bssm_fast_dmvnorm', PACKAGE = 'bssm', x, mean, Linv, nonzero, constant) -} - -psd_chol <- function(x) { - .Call('_bssm_psd_chol', PACKAGE = 'bssm', x) -} - -stratified_sample <- function(p, r, N) { - .Call('_bssm_stratified_sample', PACKAGE = 'bssm', p, r, N) -} - +# Generated by using Rcpp::compileAttributes() -> do not edit by hand +# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +gaussian_approx_model <- function(model_, model_type) { + .Call('_bssm_gaussian_approx_model', PACKAGE = 'bssm', model_, model_type) +} + +gaussian_approx_model_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, max_iter, conv_tol, iekf_iter) { + .Call('_bssm_gaussian_approx_model_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, max_iter, conv_tol, iekf_iter) +} + +bsf <- function(model_, nsim, seed, gaussian, model_type) { + .Call('_bssm_bsf', PACKAGE = 'bssm', model_, nsim, seed, gaussian, model_type) +} + +bsf_smoother <- function(model_, nsim, seed, gaussian, model_type) { + .Call('_bssm_bsf_smoother', PACKAGE = 'bssm', model_, nsim, seed, gaussian, model_type) +} + +bsf_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) { + .Call('_bssm_bsf_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) +} + +bsf_smoother_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) { + .Call('_bssm_bsf_smoother_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) +} + +ekf_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) { + .Call('_bssm_ekf_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) +} + +ekf_smoother_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) { + .Call('_bssm_ekf_smoother_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) +} + +ekf_fast_smoother_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) { + .Call('_bssm_ekf_fast_smoother_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter) +} + +ekpf <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) { + .Call('_bssm_ekpf', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) +} + +ekpf_smoother <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) { + .Call('_bssm_ekpf_smoother', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed) +} + +IACT <- function(x) { + .Call('_bssm_IACT', PACKAGE = 'bssm', x) +} + +importance_sample_ng <- function(model_, nsim, use_antithetic, seed, model_type) { + .Call('_bssm_importance_sample_ng', PACKAGE = 'bssm', model_, nsim, use_antithetic, seed, model_type) +} + +gaussian_kfilter <- function(model_, model_type) { + .Call('_bssm_gaussian_kfilter', PACKAGE = 'bssm', model_, model_type) +} + +gaussian_loglik <- function(model_, model_type) { + .Call('_bssm_gaussian_loglik', PACKAGE = 'bssm', model_, model_type) +} + +nongaussian_loglik <- function(model_, nsim, sampling_method, seed, model_type) { + .Call('_bssm_nongaussian_loglik', PACKAGE = 'bssm', model_, nsim, sampling_method, seed, model_type) +} + +nonlinear_loglik <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter, method) { + .Call('_bssm_nonlinear_loglik', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter, method) +} + +gaussian_mcmc <- function(model_, output_type, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, model_type, verbose) { + .Call('_bssm_gaussian_mcmc', PACKAGE = 'bssm', model_, output_type, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, model_type, verbose) +} + +nongaussian_pm_mcmc <- function(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type, verbose) { + .Call('_bssm_nongaussian_pm_mcmc', PACKAGE = 'bssm', model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type, verbose) +} + +nongaussian_da_mcmc <- function(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type, verbose) { + .Call('_bssm_nongaussian_da_mcmc', PACKAGE = 'bssm', model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type, verbose) +} + +nongaussian_is_mcmc <- function(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, is_type, model_type, approx, verbose) { + .Call('_bssm_nongaussian_is_mcmc', PACKAGE = 'bssm', model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, is_type, model_type, approx, verbose) +} + +nonlinear_pm_mcmc <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type, verbose) { + .Call('_bssm_nonlinear_pm_mcmc', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type, verbose) +} + +nonlinear_da_mcmc <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type, verbose) { + .Call('_bssm_nonlinear_da_mcmc', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type, verbose) +} + +nonlinear_ekf_mcmc <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, iekf_iter, output_type, verbose) { + .Call('_bssm_nonlinear_ekf_mcmc', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, iekf_iter, output_type, verbose) +} + +nonlinear_is_mcmc <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, is_type, sampling_method, max_iter, conv_tol, iekf_iter, output_type, approx, verbose) { + .Call('_bssm_nonlinear_is_mcmc', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, is_type, sampling_method, max_iter, conv_tol, iekf_iter, output_type, approx, verbose) +} + +R_milstein <- function(x0, L, t, theta, drift_pntr, diffusion_pntr, ddiffusion_pntr, positive, seed) { + .Call('_bssm_R_milstein', PACKAGE = 'bssm', x0, L, t, theta, drift_pntr, diffusion_pntr, ddiffusion_pntr, positive, seed) +} + +suggest_n_nongaussian <- function(model_, theta, candidates, replications, seed, model_type) { + .Call('_bssm_suggest_n_nongaussian', PACKAGE = 'bssm', model_, theta, candidates, replications, seed, model_type) +} + +suggest_n_nonlinear <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, theta_map, candidates, replications, seed) { + .Call('_bssm_suggest_n_nonlinear', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, theta_map, candidates, replications, seed) +} + +postcorrection_nongaussian <- function(model_, model_type, output_type, nsim, seed, n_threads, is_type, counts, theta, modes) { + .Call('_bssm_postcorrection_nongaussian', PACKAGE = 'bssm', model_, model_type, output_type, nsim, seed, n_threads, is_type, counts, theta, modes) +} + +postcorrection_nonlinear <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta_init, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, output_type, nsim, seed, n_threads, is_type, counts, theta, modes) { + .Call('_bssm_postcorrection_nonlinear', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta_init, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, output_type, nsim, seed, n_threads, is_type, counts, theta, modes) +} + +gaussian_predict <- function(model_, theta, alpha, predict_type, seed, model_type) { + .Call('_bssm_gaussian_predict', PACKAGE = 'bssm', model_, theta, alpha, predict_type, seed, model_type) +} + +nongaussian_predict <- function(model_, theta, alpha, predict_type, seed, model_type) { + .Call('_bssm_nongaussian_predict', PACKAGE = 'bssm', model_, theta, alpha, predict_type, seed, model_type) +} + +nonlinear_predict <- function(y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed) { + .Call('_bssm_nonlinear_predict', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed) +} + +gaussian_predict_past <- function(model_, theta, alpha, predict_type, seed, model_type) { + .Call('_bssm_gaussian_predict_past', PACKAGE = 'bssm', model_, theta, alpha, predict_type, seed, model_type) +} + +nongaussian_predict_past <- function(model_, theta, alpha, predict_type, seed, model_type) { + .Call('_bssm_nongaussian_predict_past', PACKAGE = 'bssm', model_, theta, alpha, predict_type, seed, model_type) +} + +nonlinear_predict_past <- function(y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed) { + .Call('_bssm_nonlinear_predict_past', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed) +} + +gaussian_psi_smoother <- function(model_, nsim, seed, model_type) { + .Call('_bssm_gaussian_psi_smoother', PACKAGE = 'bssm', model_, nsim, seed, model_type) +} + +psi_smoother <- function(model_, nsim, seed, model_type) { + .Call('_bssm_psi_smoother', PACKAGE = 'bssm', model_, nsim, seed, model_type) +} + +psi_smoother_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter) { + .Call('_bssm_psi_smoother_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter) +} + +loglik_sde <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) { + .Call('_bssm_loglik_sde', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) +} + +bsf_sde <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) { + .Call('_bssm_bsf_sde', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) +} + +bsf_smoother_sde <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) { + .Call('_bssm_bsf_smoother_sde', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed) +} + +sde_pm_mcmc <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type, verbose) { + .Call('_bssm_sde_pm_mcmc', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type, verbose) +} + +sde_da_mcmc <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type, verbose) { + .Call('_bssm_sde_da_mcmc', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type, verbose) +} + +sde_is_mcmc <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, is_type, n_threads, type, verbose) { + .Call('_bssm_sde_is_mcmc', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, is_type, n_threads, type, verbose) +} + +sde_state_sampler_bsf_is2 <- function(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, nsim, L_f, seed, approx_loglik_storage, theta) { + .Call('_bssm_sde_state_sampler_bsf_is2', PACKAGE = 'bssm', y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, nsim, L_f, seed, approx_loglik_storage, theta) +} + +gaussian_smoother <- function(model_, model_type) { + .Call('_bssm_gaussian_smoother', PACKAGE = 'bssm', model_, model_type) +} + +gaussian_ccov_smoother <- function(model_, model_type) { + .Call('_bssm_gaussian_ccov_smoother', PACKAGE = 'bssm', model_, model_type) +} + +gaussian_fast_smoother <- function(model_, model_type) { + .Call('_bssm_gaussian_fast_smoother', PACKAGE = 'bssm', model_, model_type) +} + +gaussian_sim_smoother <- function(model_, nsim, use_antithetic, seed, model_type) { + .Call('_bssm_gaussian_sim_smoother', PACKAGE = 'bssm', model_, nsim, use_antithetic, seed, model_type) +} + +ukf_nlg <- function(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, alpha, beta, kappa) { + .Call('_bssm_ukf_nlg', PACKAGE = 'bssm', y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, alpha, beta, kappa) +} + +conditional_cov <- function(Vt, Ct, use_svd) { + invisible(.Call('_bssm_conditional_cov', PACKAGE = 'bssm', Vt, Ct, use_svd)) +} + +dmvnorm <- function(x, mean, sigma, lwr, logd) { + .Call('_bssm_dmvnorm', PACKAGE = 'bssm', x, mean, sigma, lwr, logd) +} + +precompute_dmvnorm <- function(sigma, Linv, nonzero) { + .Call('_bssm_precompute_dmvnorm', PACKAGE = 'bssm', sigma, Linv, nonzero) +} + +fast_dmvnorm <- function(x, mean, Linv, nonzero, constant) { + .Call('_bssm_fast_dmvnorm', PACKAGE = 'bssm', x, mean, Linv, nonzero, constant) +} + +psd_chol <- function(x) { + .Call('_bssm_psd_chol', PACKAGE = 'bssm', x) +} + +stratified_sample <- function(p, r, N) { + .Call('_bssm_stratified_sample', PACKAGE = 'bssm', p, r, N) +} + diff --git a/R/approx.R b/R/approx.R index b2481b4a..d140ca91 100644 --- a/R/approx.R +++ b/R/approx.R @@ -1,17 +1,33 @@ #' Gaussian Approximation of Non-Gaussian/Non-linear State Space Model #' -#' Returns the approximating Gaussian model. This function is rarely needed itself, -#' and is mainly available for testing and debugging purposes. +#' Returns the approximating Gaussian model which has the same conditional +#' mode of p(alpha|y, theta) as the original model. +#' This function is rarely needed itself, and is mainly available for +#' testing and debugging purposes. #' -#' @param model Model to be approximated. -#' @param max_iter Maximum number of iterations. -#' @param conv_tol Tolerance parameter. -#' @param iekf_iter For non-linear models, number of iterations in iterated EKF (defaults to 0). +#' @param model Model to be approximated. Should be of class +#' \code{bsm_ng}, \code{ar1_ng} \code{svm}, +#' \code{ssm_ung}, or \code{ssm_mng}, or \code{ssm_nlg}, i.e. non-gaussian or +#' non-linear \code{bssm_model}. +#' @param max_iter Maximum number of iterations as a positive integer. +#' Default is 100 (although typically only few iterations are needed). +#' @param conv_tol Positive tolerance parameter. Default is 1e-8. Approximation +#' is claimed to be converged when the mean squared difference of the modes of +#' is less than \code{conv_tol}. +#' @param iekf_iter For non-linear models, non-negative number of iterations in +#' iterated EKF (defaults to 0, i.e. normal EKF). Used only for models of class +#' \code{ssm_nlg}. #' @param ... Ignored. +#' @return Returns linear-Gaussian SSM of class \code{ssm_ulg} or +#' \code{ssm_mlg} which has the same conditional mode of p(alpha|y, theta) as +#' the original model. #' @references -#' Koopman, S.J. and Durbin J. (2012). Time Series Analysis by State Space Methods. Second edition. Oxford: Oxford University Press. -#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -#' Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +#' Koopman, SJ and Durbin J (2012). Time Series Analysis by State Space +#' Methods. Second edition. Oxford: Oxford University Press. +#' +#' Vihola, M, Helske, J, Franks, J. (2020). Importance sampling type estimators +#' based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 #' @export #' @rdname gaussian_approx #' @examples @@ -19,30 +35,39 @@ #' model <- bsm_ng(y = poisson_series, sd_slope = 0.01, sd_level = 0.1, #' distribution = "poisson") #' out <- gaussian_approx(model) +#' for(i in 1:7) +#' cat("Number of iterations used: ", i, ", y[1] = ", +#' gaussian_approx(model, max_iter = i, conv_tol = 0)$y[1], "\n", sep ="") +#' gaussian_approx <- function(model, max_iter, conv_tol, ...) { UseMethod("gaussian_approx", model) } #' @rdname gaussian_approx #' @method gaussian_approx nongaussian #' @export -gaussian_approx.nongaussian <- function(model, max_iter = 100, conv_tol = 1e-8, ...) { +gaussian_approx.nongaussian <- function(model, max_iter = 100, + conv_tol = 1e-8, ...) { + + check_missingness(model) + + model$max_iter <- check_intmax(max_iter, "max_iter", positive = FALSE) + model$conv_tol <- check_positive_real(conv_tol, "conv_tol") - model$max_iter <- max_iter - model$conv_tol <- conv_tol model$distribution <- pmatch(model$distribution, c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), duplicates.ok = TRUE) - 1 out <- gaussian_approx_model(model, model_type(model)) - if(ncol(out$y) == 1) { + if (ncol(out$y) == 1L) { out$y <- ts(c(out$y), start = start(model$y), end = end(model$y), frequency = frequency(model$y)) D <- model$D - if(length(model$beta) > 0) D <- as.numeric(D) + t(model$xreg %*% model$beta) + if (length(model$beta) > 0) + D <- as.numeric(D) + t(model$xreg %*% model$beta) approx_model <- ssm_ulg(y = out$y, Z = model$Z, H = out$H, T = model$T, R = model$R, a1 = model$a1, P1 = model$P1, init_theta = model$theta, - D = D, C = model$C, state_names = names(model$a1), update_fn = model$update_fn, - prior_fn = model$prior_fn) + D = D, C = model$C, state_names = names(model$a1), + update_fn = model$update_fn, prior_fn = model$prior_fn) } else { out$y <- ts(t(out$y), start = start(model$y), end = end(model$y), frequency = frequency(model$y)) @@ -59,9 +84,11 @@ gaussian_approx.nongaussian <- function(model, max_iter = 100, conv_tol = 1e-8, gaussian_approx.ssm_nlg <- function(model, max_iter = 100, conv_tol = 1e-8, iekf_iter = 0, ...) { - model$max_iter <- max_iter - model$conv_tol <- conv_tol - model$iekf_iter <- iekf_iter + check_missingness(model) + + model$max_iter <- check_intmax(max_iter, "max_iter", positive = FALSE) + model$conv_tol <- check_positive_real(conv_tol, "conv_tol") + model$iekf_iter <- check_intmax(iekf_iter, "iekf_iter", positive = FALSE) out <- gaussian_approx_model_nlg(t(model$y), model$Z, model$H, model$T, model$R, model$Z_gn, model$T_gn, model$a1, model$P1, @@ -70,7 +97,8 @@ gaussian_approx.ssm_nlg <- function(model, max_iter = 100, as.integer(model$time_varying), max_iter, conv_tol, iekf_iter) - out$y <- ts(t(out$y), start = start(model$y), end = end(model$y), frequency = frequency(model$y)) + out$y <- ts(t(out$y), start = start(model$y), end = end(model$y), + frequency = frequency(model$y)) ssm_mlg(y = out$y, Z = out$Z, H = out$H, T = out$T, R = out$R, a1 = c(out$a1), P1 = out$P1, init_theta = model$theta, D = out$D, C = out$C) diff --git a/R/as.data.frame.mcmc_output.R b/R/as.data.frame.mcmc_output.R index 124648c3..c99713bc 100644 --- a/R/as.data.frame.mcmc_output.R +++ b/R/as.data.frame.mcmc_output.R @@ -1,19 +1,27 @@ -#' Convert MCMC chain to data.frame +#' Convert MCMC Output to data.frame #' -#' Converts the MCMC chain output of \code{\link{run_mcmc}} to data.frame. +#' Converts the MCMC output of \code{\link{run_mcmc}} to \code{data.frame}. #' #' @method as.data.frame mcmc_output -#' @param x Output from \code{\link{run_mcmc}}. +#' @param x Object of class \code{mcmc_output} from \code{\link{run_mcmc}}. #' @param row.names Ignored. #' @param optional Ignored. -#' @param variable Return samples of \code{"theta"} (default) or \code{"states"}? -#' @param times Vector of indices. In case of states, what time points to return? Default is all. -#' @param states Vector of indices. In case of states, what states to return? Default is all. +#' @param variable Return samples of \code{"theta"} (default) or +#' \code{"states"}? +#' @param times A vector of indices. In case of states, +#' what time points to return? Default is all. +#' @param states A vector of indices. In case of states, +#' what states to return? Default is all. #' @param expand Should the jump-chain be expanded? -#' Defaults to \code{TRUE} for non-IS-MCMC, and \code{FALSE} for IS-MCMC. +#' Defaults to \code{TRUE}. #' For \code{expand = FALSE} and always for IS-MCMC, -#' the resulting data.frame contains variable weight (= counts times IS-weights). +#' the resulting data.frame contains variable weight (= counts * IS-weights). +#' @param use_times If \code{TRUE} (default), transforms the values of the time +#' variable to match the ts attribute of the input to define. If \code{FALSE}, +#' time is based on the indexing starting from 1. #' @param ... Ignored. +#' @seealso \code{as_draws} which converts the output for +#' \code{as_draws} object. #' @export #' @examples #' data("poisson_series") @@ -30,46 +38,57 @@ #' head(as.data.frame(out, variable = "theta", expand = FALSE)) #' #' # IS-weighted version: -#' out_is <- run_mcmc(model, iter = 2000, particles = 10, mcmc_type = "is2") +#' out_is <- run_mcmc(model, iter = 2000, particles = 10, +#' mcmc_type = "is2") #' head(as.data.frame(out_is, variable = "theta")) #' as.data.frame.mcmc_output <- function(x, row.names, optional, variable = c("theta", "states"), times, states, - expand = !(x$mcmc_type %in% paste0("is", 1:3)), ...) { + expand = TRUE, + use_times = TRUE, ...) { - variable <- match.arg(variable, c("theta", "states")) + variable <- match.arg(tolower(variable), c("theta", "states")) if (variable == "theta") { if (expand) { values <- suppressWarnings(expand_sample(x, "theta")) iters <- seq(x$burnin + 1, x$iter, by = x$thin) - weights <- if(x$mcmc_type %in% paste0("is", 1:3)) rep(x$weights, times = x$counts) else 1 + weights <- if (x$mcmc_type %in% paste0("is", 1:3)) { + rep(x$weights, times = x$counts) + } else 1 } else { values <- x$theta iters <- x$burnin + cumsum(x$counts) - weights <- x$counts * (if(x$mcmc_type %in% paste0("is", 1:3)) x$weights else 1) + weights <- + x$counts * (if (x$mcmc_type %in% paste0("is", 1:3)) x$weights else 1) } d <- data.frame(iter = iters, value = as.numeric(values), variable = rep(colnames(values), each = nrow(values)), weight = weights) } else { - if (missing(times)) times <- 1:nrow(x$alpha) - if (missing(states)) states <- 1:ncol(x$alpha) + if (missing(times)) times <- seq_len(nrow(x$alpha)) + if (missing(states)) states <- seq_len(ncol(x$alpha)) if (expand) { - values <- aperm(x$alpha[times, states, rep(1:nrow(x$theta), times = x$counts), drop = FALSE], 3:1) + values <- aperm(x$alpha[times, states, + rep(seq_len(nrow(x$theta)), times = x$counts), drop = FALSE], 3:1) iters <- seq(x$burnin + 1, x$iter, by = x$thin) - weights <- if(x$mcmc_type %in% paste0("is", 1:3)) rep(x$weights, times = x$counts) else 1 + weights <- if (x$mcmc_type %in% paste0("is", 1:3)) { + rep(x$weights, times = x$counts) + } else 1 } else { values <- aperm(x$alpha[times, states, , drop = FALSE], 3:1) iters <- x$burnin + cumsum(x$counts) - weights <- x$counts * (if(x$mcmc_type %in% paste0("is", 1:3)) x$weights else 1) + weights <- x$counts * + (if (x$mcmc_type %in% paste0("is", 1:3)) x$weights else 1) + } + if (use_times) { + times <- time(ts(seq_len(nrow(x$alpha)), + start = attr(x, "ts")$start, + frequency = attr(x, "ts")$frequency))[times] } - times <- time(ts(1:nrow(x$alpha), - start = attr(x, "ts")$start, - frequency = attr(x, "ts")$frequency))[times] d <- data.frame(iter = iters, value = as.numeric(values), variable = rep(colnames(x$alpha)[states], each = nrow(values)), @@ -78,4 +97,3 @@ as.data.frame.mcmc_output <- function(x, } d } - diff --git a/R/as_bssm.R b/R/as_bssm.R index 502854c1..df39fe31 100644 --- a/R/as_bssm.R +++ b/R/as_bssm.R @@ -1,25 +1,31 @@ #' Convert KFAS Model to bssm Model #' -#' Converts \code{SSModel} object of \code{KFAS} package to general -#' \code{bssm} model of type \code{ssm_ulg}, \code{ssm_mlg}, \code{ssm_ung} or \code{ssm_mng}. +#' Converts \code{SSModel} object of \code{KFAS} package to general \code{bssm} +#' model of type \code{ssm_ulg}, \code{ssm_mlg}, \code{ssm_ung} or +#' \code{ssm_mng}. As \code{KFAS} supports formula syntax for defining +#' e.g. regression and cyclic components it maybe sometimes easier to define +#' the model with \code{KFAS::SSModel} and then convert for the bssm style with +#' \code{as_bssm}. #' #' @param model Object of class \code{SSModel}. #' @param kappa For \code{SSModel} object, a prior variance for initial state #' used to replace exact diffuse elements of the original model. #' @param ... Additional arguments to model building functions of \code{bssm} -#' (such as prior and updating functions). -#' @return Object of class \code{ssm_ulg}, \code{ssm_mlg}, \code{ssm_ung} or \code{ssm_mng}. +#' (such as prior and updating functions, C, and D). +#' @return An object of class \code{ssm_ulg}, \code{ssm_mlg}, \code{ssm_ung} or +#' \code{ssm_mng}. #' @export #' @examples #' library("KFAS") -#' model_KFAS <- SSModel(Nile ~ -#' SSMtrend(1, Q = 2, P1 = 1e4), H = 2) -#' model_bssm <- as_bssm(model_KFAS) -#' logLik(model_KFAS) -#' logLik(model_bssm) +#' model_KFAS <- SSModel(Nile ~ +#' SSMtrend(1, Q = 2, P1 = 1e4), H = 2) +#' model_bssm <- as_bssm(model_KFAS) +#' logLik(model_KFAS) +#' logLik(model_bssm) #' as_bssm <- function(model, kappa = 100, ...) { + kappa <- check_positive_real(kappa, "kappa") if (!requireNamespace("KFAS", quietly = TRUE)) { stop("This function depends on the KFAS package. ", call. = FALSE) } @@ -35,28 +41,30 @@ as_bssm <- function(model, kappa = 100, ...) { if (dim(model$R)[2] > 1) { for (i in 1:dim(R)[3]) { L <- KFAS::ldl(model$Q[, , (i - 1) * tvq + 1]) - D <- sqrt(diag(diag(L))) + d <- sqrt(diag(diag(L))) diag(L) <- 1 - R[, , i] <- model$R[, , (i - 1) * tvr + 1] %*% L %*% D + R[, , i] <- model$R[, , (i - 1) * tvr + 1] %*% L %*% d } } else { R <- model$R * sqrt(c(model$Q)) } - if (attr(model, "p") == 1) { + if (attr(model, "p") == 1L) { Z <- aperm(model$Z, c(2, 3, 1)) dim(Z) <- dim(Z)[1:2] } else { - Z = model$Z + Z <- model$Z } if (any(model$distribution != "gaussian")) { - if (attr(model, "p") == 1) { - - if (model$distribution == "negative binomial" && length(unique(model$u)) > 1) { - stop("Time-varying dispersion parameter for negative binomial is not (yet) supported in 'bssm'.") + if (attr(model, "p") == 1L) { + if (model$distribution == "negative binomial" && + length(unique(model$u)) > 1L) { + stop(paste("Time-varying dispersion parameter for negative binomial", + "is not (yet) supported in 'bssm'.", sep = " ")) } - if (model$distribution == "gamma" && length(unique(model$u)) > 1) { - stop("Time-varying shape parameter for gamma is not (yet) supported in 'bssm'.") + if (model$distribution == "gamma" && length(unique(model$u)) > 1L) { + stop(paste("Time-varying shape parameter for gamma is not (yet)", + "supported in 'bssm'.", sep = " ")) } switch(model$distribution, @@ -83,33 +91,37 @@ as_bssm <- function(model, kappa = 100, ...) { } else { phi <- numeric(attr(model, "p")) u <- model$u - for(i in 1:attr(model, "p")) { + for (i in 1:attr(model, "p")) { switch(model$distribution[i], poisson = { phi[i] <- 1 - u[,i] <- model$u[,i] + u[, i] <- model$u[, i] }, binomial = { phi[i] <- 1 - u[,i] <- model$u[,i] + u[, i] <- model$u[, i] }, gamma = { - if(length(unique(model$u[,i])) > 1) - stop("Time-varying shape parameter for gamma is not (yet) supported in 'bssm'.") - phi[i] <- model$u[1,i] - u[,i] <- 1 # no exposure for Gamma in KFAS + if (length(unique(model$u[, i])) > 1) + stop(paste0("Time-varying shape parameter for gamma is not", + "(yet) supported in 'bssm'.", sep = " ")) + phi[i] <- model$u[1, i] + u[, i] <- 1 # no exposure for Gamma in KFAS }, "negative binomial" = { - if(length(unique(model$u[,i])) > 1) - stop("Time-varying dispersion parameter for negative binomial is not (yet) supported in 'bssm'.") - phi[i] <- model$u[1,i] - u[,i] <- 1 # no exposure for NB in KFAS + if (length(unique(model$u[, i])) > 1) + stop(paste("Time-varying dispersion parameter for negative", + "binomial is not (yet) supported in 'bssm'.", sep = " ")) + phi[i] <- model$u[1, i] + u[, i] <- 1 # no exposure for NB in KFAS }, gaussian = { - if(length(unique(model$u[,i])) > 1) - stop("Time-varying standard deviation for gaussian distribution with non-gaussian series is not supported in 'bssm'.") - phi[i] <- sqrt(model$u[1,i]) - u[,i] <- 1 + if (length(unique(model$u[, i])) > 1) + stop(paste("Time-varying standard deviation for gaussian", + "distribution with non-gaussian series is not supported", + "in 'bssm'.", sep = " ")) + phi[i] <- sqrt(model$u[1, i]) + u[, i] <- 1 }) } @@ -120,17 +132,17 @@ as_bssm <- function(model, kappa = 100, ...) { } } else { - if (attr(model, "p") == 1) { - H = sqrt(c(model$H)) - out <- ssm_ulg(y = model$y, Z =Z, H = H, T = model$T, R = R, + if (attr(model, "p") == 1L) { + out <- ssm_ulg(y = model$y, Z = Z, H = sqrt(c(model$H)), T = model$T, + R = R, a1 = c(model$a1), P1 = model$P1, state_names = rownames(model$a1), ...) } else { H <- model$H for (i in 1:dim(H)[3]) { L <- KFAS::ldl(model$H[, , i]) - D <- sqrt(diag(diag(L))) + d <- sqrt(diag(diag(L))) diag(L) <- 1 - H[, , i] <- L %*% D + H[, , i] <- L %*% d } out <- ssm_mlg(y = model$y, Z = Z, H = H, T = model$T, R = R, @@ -140,4 +152,3 @@ as_bssm <- function(model, kappa = 100, ...) { out } - diff --git a/R/as_draws.R b/R/as_draws.R new file mode 100644 index 00000000..523b78e5 --- /dev/null +++ b/R/as_draws.R @@ -0,0 +1,116 @@ +#' Convert \code{run_mcmc} Output to \code{draws_df} Format +#' +#' Converts MCMC output from \code{run_mcmc} call to a +#' \code{draws_df} format of the \code{posterior} package. This enables the use +#' of diagnostics and plotting methods of \code{posterior} and \code{bayesplot} +#' packages. +#' +#' @note The jump chain representation is automatically expanded by +#' \code{as_draws}, but if \code{run_mcmc} used IS-MCMC method, the output +#' contains additional \code{weight} column corresponding to the IS-weights +#' (without counts), which is ignored by \code{posterior} and \code{bayesplot}, +#' i.e. those results correspond to approximate MCMC. +#' +#' @param x An object of class \code{mcmc_output}. +#' @param times A vector of indices defining which time points to return? +#' Default is all. If 0, no samples for the states are extracted. +#' @param states A vector of indices defining which states to return. +#' Default is all. If 0, no samples for the states are extracted. +#' @param ... Ignored. +#' @return A \code{draws_df} object. +#' @importFrom posterior as_draws as_draws_df +#' @importFrom tidyr pivot_wider +#' @aliases as_draws as_draws_df +#' @export +#' @export as_draws_df +#' @rdname as_draws-mcmc_output +#' @method as_draws_df mcmc_output +#' @examples +#' +#' model <- bsm_lg(Nile, +#' sd_y = tnormal(init = 100, mean = 100, sd = 100, min = 0), +#' sd_level = tnormal(init = 50, mean = 50, sd = 100, min = 0), +#' a1 = 1000, P1 = 500^2) +#' +#' fit1 <- run_mcmc(model, iter = 2000) +#' draws <- as_draws(fit1) +#' head(draws, 4) +#' estimate_ess(draws$sd_y) +#' summary(fit1, return_se = TRUE) +#' +#' # More chains: +#' model$theta[] <- c(50, 150) # change initial value +#' fit2 <- run_mcmc(model, iter = 2000, verbose = FALSE) +#' model$theta[] <- c(150, 50) # change initial value +#' fit3 <- run_mcmc(model, iter = 2000, verbose = FALSE) +#' +#' # it is actually enough to transform first mcmc_output to draws object, +#' # rest are transformed automatically inside bind_draws +#' draws <- posterior::bind_draws(as_draws(fit1), +#' as_draws(fit2), as_draws(fit3), along = "chain") +#' +#' posterior::rhat(draws$sd_y) +#' +as_draws_df.mcmc_output <- function(x, times, states, ...) { + + d_theta <- as.data.frame(x, variable = "theta", expand = TRUE) + + if (missing(times)) { + times <- seq_len(nrow(x$alpha)) + } else { + if (!identical(times, 0)) { + if (!test_integerish(times, lower = 1, upper = nrow(x$alpha), + any.missing = FALSE, unique = TRUE)) { + stop("Argument 'times' should contain indices between 1 and ", + nrow(x$alpha), ", or it should be a scalar 0.") + } + } + } + if (missing(states)) { + states <- seq_len(ncol(x$alpha)) + } else { + if (!identical(states, 0)) { + if (!test_integerish(states, lower = 1, upper = ncol(x$alpha), + any.missing = FALSE, unique = TRUE)) + stop("Argument 'states' should contain indices between 1 and ", + ncol(x$alpha)," or it should be a scalar 0.") + } + } + if (identical(times, 0) || identical(states, 0)) { + d <- + tidyr::pivot_wider(d_theta, + values_from = .data$value, + names_from = .data$variable) + } else { + d_states <- as.data.frame(x, variable = "states", expand = TRUE, + times = times, states = states, use_times = FALSE) + d <- cbind( + tidyr::pivot_wider(d_theta, + values_from = .data$value, + names_from = .data$variable), + tidyr::pivot_wider(d_states, + values_from = .data$value, + names_from = c(.data$variable, .data$time), + names_glue = "{variable}[{time}]")[, -(1:2)]) + } + + names(d)[1] <- ".iteration" + + if (x$mcmc_type %in% paste0("is", 1:3)) { + warning(paste("Input is based on a IS-MCMC and the output column 'weight'", + "contains the IS-weights. These are not used for example in the", + "diagnostic methods by 'posterior' package, i.e. these are based", + "on approximate MCMC chains.")) + } else { + d$weight <- NULL + } + + as_draws(d) +} +#' @export +#' @export as_draws +#' @rdname as_draws-mcmc_output +#' @method as_draws mcmc_output +as_draws.mcmc_output <- function(x, times, states, ...) { + as_draws_df.mcmc_output(x, times, states, ...) +} diff --git a/R/asymptotic_var.R b/R/asymptotic_var.R new file mode 100644 index 00000000..58e64e6e --- /dev/null +++ b/R/asymptotic_var.R @@ -0,0 +1,171 @@ +#' Integrated Autocorrelation Time +#' +#' Estimates the integrated autocorrelation time (IACT) based on Sokal (1997). +#' Note that the estimator is not particularly good for very short series x +#' (say < 100), but that is not very practical for MCMC applications anyway. +#' +#' @param x A numeric vector. +#' @return A single numeric value of IACT estimate. +#' @references +#' Sokal A. (1997) Monte Carlo Methods in Statistical Mechanics: Foundations +#' and New Algorithms. +#' In: DeWitt-Morette C., Cartier P., Folacci A. (eds) Functional Integration. +#' NATO ASI Series (Series B: Physics), vol 361. Springer, Boston, MA. +#' https://doi.org/10.1007/978-1-4899-0319-8_6 +#' @export +#' @srrstats {BS5.3, BS5.5} +#' @examples +#' set.seed(1) +#' n <- 1000 +#' x <- numeric(n) +#' phi <- 0.8 +#' for(t in 2:n) x[t] <- phi * x[t-1] + rnorm(1) +#' iact(x) +iact <- function(x) { + + if (!test_numeric(x)) + stop("Argument 'x' should be a numeric vector. ") + + IACT((x - mean(x)) / sd(x)) +} + +#' Asymptotic Variance of IS-type Estimators +#' +#' The asymptotic variance MCMCSE^2 is based on Corollary 1 +#' of Vihola et al. (2020) from weighted samples from IS-MCMC. The default +#' method is based on the integrated autocorrelation time (IACT) by Sokal +#' (1997) which seem to work well for reasonable problems, but it is also +#' possible to use the Geyer's method as implemented in \code{ess_mean} of the +#' \code{posterior} package. +#' +#' @importFrom posterior ess_mean +#' @importFrom checkmate test_numeric +#' @param x A numeric vector of samples. +#' @param w A numeric vector of weights. If missing, set to 1 (i.e. no +#' weighting is assumed). +#' @param method Method for computing IACT. Default is \code{"sokal"}, +#' other option \code{"geyer"}. +#' @return A single numeric value of asymptotic variance estimate. +#' @references +#' Vihola M, Helske J, Franks J. (2020). Importance sampling type estimators +#' based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 +#' +#' Sokal A. (1997). Monte Carlo Methods in Statistical Mechanics: Foundations +#' and New Algorithms. +#' In: DeWitt-Morette C, Cartier P, Folacci A (eds) Functional Integration. +#' NATO ASI Series (Series B: Physics), vol 361. Springer, Boston, MA. +#' https://doi.org/10.1007/978-1-4899-0319-8_6 +#' +#' Gelman, A, Carlin J B, Stern H S, Dunson, D B, Vehtari A, Rubin D B. (2013). +#' Bayesian Data Analysis, Third Edition. Chapman and Hall/CRC. +#' +#' Vehtari A, Gelman A, Simpson D, Carpenter B, Bürkner P-C. (2021). +#' Rank-normalization, folding, and localization: An improved Rhat for +#' assessing convergence of MCMC. Bayesian analysis, 16(2):667-718. +#' https://doi.org/10.1214/20-BA1221 +#' @export +#' @srrstats {BS5.3, BS5.5} +#' @examples +#' set.seed(1) +#' n <- 1e4 +#' x <- numeric(n) +#' phi <- 0.7 +#' for(t in 2:n) x[t] <- phi * x[t-1] + rnorm(1) +#' w <- rexp(n, 0.5 * exp(0.001 * x^2)) +#' # different methods: +#' asymptotic_var(x, w, method = "sokal") +#' asymptotic_var(x, w, method = "geyer") +#' +#' data("negbin_model") +#' # can be obtained directly with summary method +#' d <- suppressWarnings(as_draws(negbin_model)) +#' sqrt(asymptotic_var(d$sd_level, d$weight)) +#' +asymptotic_var <- function(x, w, method = "sokal") { + + method <- match.arg(tolower(method), c("sokal", "geyer")) + if (!test_numeric(x) & !is.null(class(x))) + stop("Argument 'x' should be a numeric vector. ") + if (missing(w)) { + w <- rep(1, length(x)) + } else { + if (!test_numeric(w)) + stop("Argument 'w' should be a numeric vector. ") + if(any(w < 0) | any(!is.finite(w))) + stop("Nonfinite or negative weights in 'w'.") + if (!any(w > 0)) { + stop("No positive weights in 'w'.") + } + } + estimate_c <- mean(w) + estimate_mean <- weighted_mean(x, w) + z <- w * (x - estimate_mean) + switch(method, + sokal = (var(z) * iact(z) / estimate_c^2) / length(z), + # ESS(z) = n / IACT(z) + geyer = var(z) / ess_mean(z) / estimate_c^2) +} + +#' Effective Sample Size for IS-type Estimators +#' +#' Computes the effective sample size (ESS) based on weighted posterior +#' samples. +#' +#' The asymptotic variance MCMCSE^2 is based on Corollary 1 of +#' Vihola et al. (2020) which is used to compute an estimate for the ESS +#' using the identity ESS(x) = var(x) / MCMCSE^2 where var(x) is the +#' posterior variance of x assuming independent samples. +#' +#' @param x A numeric vector of samples. +#' @param w A numeric vector of weights. If missing, set to 1 (i.e. no +#' weighting is assumed). +#' @param method Method for computing the ESS. Default is \code{"sokal"}, other +#' option are \code{"geyer"} (see also \code{asymptotic_var}). +#' @references +#' Vihola, M, Helske, J, Franks, J. (2020). Importance sampling type estimators +#' based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 +#' +#' Sokal A. (1997). Monte Carlo Methods in Statistical Mechanics: Foundations +#' and New Algorithms. +#' In: DeWitt-Morette C, Cartier P, Folacci A (eds) Functional Integration. +#' NATO ASI Series (Series B: Physics), vol 361. Springer, Boston, MA. +#' https://doi.org/10.1007/978-1-4899-0319-8_6 +#' +#' Gelman, A, Carlin J B, Stern H S, Dunson, D B, Vehtari A, Rubin D B. (2013). +#' Bayesian Data Analysis, Third Edition. Chapman and Hall/CRC. +#' @export +#' @srrstats {BS5.3, BS5.5} +#' @return A single numeric value of effective sample size estimate. +#' @examples +#' set.seed(1) +#' n <- 1e4 +#' x <- numeric(n) +#' phi <- 0.7 +#' for(t in 2:n) x[t] <- phi * x[t-1] + rnorm(1) +#' w <- rexp(n, 0.5 * exp(0.001 * x^2)) +#' # different methods: +#' estimate_ess(x, w, method = "sokal") +#' estimate_ess(x, w, method = "geyer") +#' +estimate_ess <- function(x, w, method = "sokal") { + + method <- match.arg(tolower(method), c("sokal", "geyer")) + + if (!test_numeric(x)) + stop("Argument 'x' should be a numeric vector. ") + + if (missing(w)) { + w <- rep(1, length(x)) + } else { + if (!test_numeric(w)) + stop("Argument 'w' should be a numeric vector. ") + if(any(w < 0) | any(!is.finite(w))) + stop("Nonfinite or negative weights in 'w'.") + if (!any(w > 0)) { + stop("No positive weights in 'w'.") + } + } + weighted_var(x, w) / asymptotic_var(x, w, method = method) +} diff --git a/R/bootstrap_filter.R b/R/bootstrap_filter.R index 6b2e1b7c..bc056353 100644 --- a/R/bootstrap_filter.R +++ b/R/bootstrap_filter.R @@ -1,23 +1,30 @@ #' Bootstrap Filtering #' -#' Function \code{bootstrap_filter} performs a bootstrap filtering with stratification -#' resampling. -#' @param model of class \code{bsm_lg}, \code{bsm_ng} or \code{svm}. -#' @param particles Number of particles. -#' @param seed Seed for RNG. +#' Function \code{bootstrap_filter} performs a bootstrap filtering with +#' stratification resampling. +#' +#' @param model A model object of class \code{bssm_model}. +#' @param particles Number of particles as a positive integer. Suitable values +#' depend on the model and the data, and while larger values provide more +#' accurate estimates, the run time also increases with respect to the +#' number of particles, so it is generally a good idea to test the filter first +#' with a small number of particles, e.g., less than 100. +#' @param seed Seed for the C++ RNG (positive integer). #' @param ... Ignored. -#' @return List with samples (\code{alpha}) from the filtering distribution and corresponding weights (\code{weights}), -#' as well as filtered and predicted states and corresponding covariances (\code{at}, \code{att}, \code{Pt}, \code{Ptt}), -#' and estimated log-likelihood (\code{logLik}). +#' @return List with samples (\code{alpha}) from the filtering distribution and +#' corresponding weights (\code{weights}), as well as filtered and predicted +#' states and corresponding covariances (\code{at}, \code{att}, \code{Pt}, +#' \code{Ptt}), and estimated log-likelihood (\code{logLik}). #' @export #' @references -#' Gordon, N. J., Salmond, D. J., & Smith, A. F. M. (1993). -#' Novel approach to nonlinear/non-Gaussian Bayesian state estimation. IEE Proceedings-F, 140, 107–113. +#' Gordon, NJ, Salmond, DJ, Smith, AFM (1993) Novel approach to +#' nonlinear/non-Gaussian Bayesian state estimation. IEE Proceedings F, +#' 140(2), p. 107-113. #' @rdname bootstrap_filter bootstrap_filter <- function(model, particles, ...) { UseMethod("bootstrap_filter", model) } -#' @method bootstrap_filter gaussian +#' @method bootstrap_filter lineargaussian #' @rdname bootstrap_filter #' @export #' @examples @@ -30,19 +37,35 @@ bootstrap_filter <- function(model, particles, ...) { #' ts.plot(cbind(y, x, out$att), col = 1:3) #' ts.plot(cbind(kfilter(model)$att, out$att), col = 1:3) #' -bootstrap_filter.gaussian <- function(model, particles, +bootstrap_filter.lineargaussian <- function(model, particles, seed = sample(.Machine$integer.max, size = 1), ...) { - if(missing(particles)) { - particles <- match.call(expand.dots = TRUE)$particles - if (!is.null(particles)) particles <- particles + check_missingness(model) + + if (missing(particles)) { + nsim <- eval(match.call(expand.dots = TRUE)$nsim) + if (!is.null(nsim)) { + warning(paste0("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) + particles <- nsim + } } + particles <- check_intmax(particles, "particles") + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + nsamples <- ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + length(model$a1) * particles + if (particles > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } out <- bsf(model, particles, seed, TRUE, model_type(model)) colnames(out$at) <- colnames(out$att) <- colnames(out$Pt) <- - colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- names(model$a1) + colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- + names(model$a1) out$at <- ts(out$at, start = start(model$y), frequency = frequency(model$y)) - out$att <- ts(out$att, start = start(model$y), frequency = frequency(model$y)) + out$att <- ts(out$att, start = start(model$y), + frequency = frequency(model$y)) rownames(out$alpha) <- names(model$a1) out$alpha <- aperm(out$alpha, c(2, 1, 3)) out @@ -62,14 +85,24 @@ bootstrap_filter.gaussian <- function(model, particles, bootstrap_filter.nongaussian <- function(model, particles, seed = sample(.Machine$integer.max, size = 1), ...) { - if(missing(particles)) { + check_missingness(model) + + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste0("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } - + particles <- check_intmax(particles, "particles") + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + nsamples <- ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + length(model$a1) * particles + if (particles > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } model$distribution <- pmatch(model$distribution, c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), @@ -77,7 +110,8 @@ bootstrap_filter.nongaussian <- function(model, particles, out <- bsf(model, particles, seed, FALSE, model_type(model)) colnames(out$at) <- colnames(out$att) <- colnames(out$Pt) <- - colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- names(model$a1) + colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- + names(model$a1) out$at <- ts(out$at, start = start(model$y), frequency = frequency(model$y)) out$att <- ts(out$att, start = start(model$y), frequency = frequency(model$y)) rownames(out$alpha) <- names(model$a1) @@ -89,14 +123,26 @@ bootstrap_filter.nongaussian <- function(model, particles, #' @export bootstrap_filter.ssm_nlg <- function(model, particles, seed = sample(.Machine$integer.max, size = 1), ...) { - - if(missing(particles)) { + + check_missingness(model) + + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } + particles <- check_intmax(particles, "particles") + + nsamples <- ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + model$n_states * particles + if (particles > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) out <- bsf_nlg(t(model$y), model$Z, model$H, model$T, model$R, model$Z_gn, model$T_gn, model$a1, model$P1, @@ -114,21 +160,34 @@ bootstrap_filter.ssm_nlg <- function(model, particles, #' @method bootstrap_filter ssm_sde #' @rdname bootstrap_filter -#' @param L Integer defining the discretization level for SDE models. +#' @param L Positive integer defining the discretization level for SDE models. #' @export bootstrap_filter.ssm_sde <- function(model, particles, L, seed = sample(.Machine$integer.max, size = 1), ...) { - if(L < 1) stop("Discretization level L must be larger than 0.") + check_missingness(model) + + if (!test_count(L, positive=TRUE)) + stop("Discretization level L must be a positive integer.") - if(missing(particles)) { + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } + particles <- check_intmax(particles, "particles") + + nsamples <- length(model$y) * particles + if (particles > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + out <- bsf_sde(model$y, model$x0, model$positive, model$drift, model$diffusion, model$ddiffusion, model$prior_pdf, model$obs_pdf, model$theta, diff --git a/R/bssm-package.R b/R/bssm-package.R index 2fd5ec7f..4478dc0a 100644 --- a/R/bssm-package.R +++ b/R/bssm-package.R @@ -1,78 +1,263 @@ +#' #' Bayesian Inference of State Space Models #' -#' This package contains functions for Bayesian inference of basic stochastic volatility model -#' and exponential family state space models, where the state equation is linear and Gaussian, -#' and the conditional observation density is either Gaussian, Poisson, -#' binomial, negative binomial or Gamma density. General non-linear Gaussian models and models -#' with continuous SDE dynamics are also supported. For formal definition of the -#' currently supported models and methods, as well as some theory behind the IS-MCMC and \eqn{\psi}{psi}-APF, -#' see the package vignettes and Vihola, Helske, Franks (2020). -#' -#' @references -#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -#' Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +#' This package contains functions for efficient Bayesian inference of state +#' space models (SSMs). For details, see the package vignette and the R Journal +#' paper. +#' +#' @details +#' The model is assumed to be either +#' +#' * Exponential family state space model, where the state equation is linear +#' Gaussian, and the conditional observation density is either Gaussian, +#' Poisson, binomial, negative binomial or Gamma density. +#' +#' * Basic stochastic volatility model. +#' +#' * General non-linear model with Gaussian noise terms. +#' +#' * Model with continuous SDE dynamics. +#' +#' Missing values in response series are allowed as per SSM theory and can be +#' automatically predicted, but there can be no missing values in the system +#' matrices of the model. +#' +#' The package contains multiple functions for building the model: +#' +#' * `bsm_lg` for basic univariate structural time series model (BSM), +#' `ar1` for univariate noisy AR(1) process, and `ssm_ulg` and `ssm_mlg` for +#' arbitrary linear gaussian model with univariate/multivariate +#' observations. +#' * The non-Gaussian versions (where observations are non-Gaussian) of the +#' above models can be constructed using the functions `bsm_ng`, `ar1_ng`, +#' `ssm_ung` and `ssm_mng`. +#' * An univariate stochastic volatility model can be defined using a function +#' `svm`. +#' * For non-linear models, user must define the model using C++ snippets and +#' the the function `ssm_nlg`. See details in the `growth_model` vignette. +#' * Diffusion models can be defined with the function `ssm_sde`, again using +#' the C++ snippets. See `sde_model` vignette for details. +#' +#' See the corresponding functions for some examples and details. +#' +#' After building the model, the model can be estimated via `run_mcmc` +#' function. The documentation of this function gives some examples. The +#' \code{bssm} package includes several MCMC sampling and sequential Monte +#' Carlo methods for models outside classic linear-Gaussian framework. For +#' definitions of the currently supported models and methods, usage of the +#' package as well as some theory behind the novel IS-MCMC and +#' \eqn{\psi}{psi}-APF algorithms, see Helske and Vihola (2021), Vihola, +#' Helske, Franks (2020), and the package vignettes. +#' +#' The output of the `run_mcmc` can be analysed by extracting the posterior +#' samples of the latent states and hyperparameters using `as.data.frame`, +#' `as_draws`, `expand_sample`, and `summary` methods, as well as `fitted` and +#' `predict` methods. Some MCMC diagnostics checks are available via +#' `check_diagnostics` function, some of which are also provided via the print +#' method of the `run_mcmc` output. Functionality of the `ggplot2` and +#' `bayesplot`, can be used to visualize the posterior draws or their summary +#' statistics, and further diagnostics checks can be performed with the help of +#' the `posterior` and `coda` packages. +#' +#' @references +#' Helske J, Vihola M (2021). bssm: Bayesian Inference of Non-linear and +#' Non-Gaussian State Space Models in R. The R Journal (2021) 13:2, 578-589. +#' https://doi.org/10.32614/RJ-2021-103 +#' +#' Vihola, M, Helske, J, Franks, J. (2020). Importance sampling type estimators +#' based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 +#' +#' H. Wickham. ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag +#' New York, 2016. +#' +#' Gabry J, Mahr T (2022). “bayesplot: Plotting for Bayesian Models.” R package +#' version 1.9.0, https://mc-stan.org/bayesplot. +#' +#' Bürkner P, Gabry J, Kay M, Vehtari A (2022). “posterior: Tools for Working +#' with Posterior Distributions.” R package version 1.2.1, +#' https://mc-stan.org/posterior. +#' +#' Martyn Plummer, Nicky Best, Kate Cowles and Karen Vines (2006). CODA: +#' Convergence Diagnosis and Output Analysis for MCMC, R News, vol 6, 7-11. #' #' @docType package #' @name bssm -#' @aliases bssm +#' @aliases bssm bssm-package #' @importFrom Rcpp evalCpp -#' @importFrom coda mcmc -#' @importFrom stats as.ts dnorm end frequency is.ts logLik quantile start time ts ts.union tsp tsp<- sd +#' @importFrom stats as.ts dnorm end frequency is.ts logLik quantile start +#' time ts ts.union tsp tsp<- sd na.omit #' @useDynLib bssm +#' @examples +#' # Create a local level model (latent random walk + noise) to the Nile +#' # dataset using the bsm_lg function: +#' model <- bsm_lg(Nile, +#' sd_y = tnormal(init = 100, mean = 100, sd = 100, min = 0), +#' sd_level = tnormal(init = 50, mean = 50, sd = 100, min = 0), +#' a1 = 1000, P1 = 500^2) +#' +#' # the priors for the unknown paramters sd_y and sd_level were defined +#' # as trunctated normal distributions, see ?bssm_prior for details +#' +#' # Run the MCMC for 2000 iterations (notice the small number of iterations to +#' # comply with the CRAN's check requirements) +#' fit <- run_mcmc(model, iter = 2000) +#' +#' # Some diagnostics checks: +#' check_diagnostics(fit) +#' +#' # print some summary information: +#' fit +#' +#' # traceplots: +#' plot(fit) +#' +#' # extract the summary statistics for state variable +#' sumr <- summary(fit,variable = "states") +#' +#' # visualize +#' library("ggplot2") +#' ggplot(sumr, aes(time, Mean)) + +#' geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`),alpha = 0.25) + +#' geom_line() + +#' theme_bw() +#' NULL -#' Deaths by drowning in Finland in 1969-2014 +#' Deaths by drowning in Finland in 1969-2019 #' -#' Dataset containing number of deaths by drowning in Finland in 1969-2014, -#' yearly average summer temperatures (June to August) and -#' corresponding population sizes (in hundreds of thousands). +#' Dataset containing number of deaths by drowning in Finland in 1969-2019, +#' corresponding population sizes (in hundreds of thousands), and +#' yearly average summer temperatures (June to August), based on simple +#' unweighted average of three weather stations: Helsinki (Southern Finland), +#' Jyvaskyla (Central Finland), and Sodankyla (Northern Finland). #' #' @name drownings #' @docType data -#' @format A time series object containing 46 observations. -#' @source Statistics Finland \url{https://pxnet2.stat.fi/PXWeb/pxweb/en/StatFin/}. +#' @format A time series object containing 51 observations. +#' @source Statistics Finland +#' \url{https://stat.fi/tup/tilastotietokannat/index_en.html}. #' @keywords datasets #' @examples #' data("drownings") #' model <- bsm_ng(drownings[, "deaths"], u = drownings[, "population"], -#' xreg = drownings[, "summer_temp"], distribution = "poisson", +#' xreg = drownings[, "summer_temp"], distribution = "poisson", #' beta = normal(0, 0, 1), -#' sd_level = gamma(0.1,2, 10), sd_slope = gamma(0, 2, 10)) -#' -#' fit <- run_mcmc(model, iter = 5000, +#' sd_level = gamma_prior(0.1,2, 10), sd_slope = gamma_prior(0, 2, 10)) +#' +#' fit <- run_mcmc(model, iter = 5000, #' output_type = "summary", mcmc_type = "approx") #' fit #' ts.plot(model$y/model$u, exp(fit$alphahat[, 1]), col = 1:2) NULL #' Pound/Dollar daily exchange rates #' -#' Dataset containing daily log-returns from 1/10/81-28/6/85 as in [1] +#' Dataset containing daily log-returns from 1/10/81-28/6/85 as in Durbin and +#' Koopman (2012). #' #' @name exchange #' @docType data #' @format A vector of length 945. -#' @source \url{http://www.ssfpack.com/DKbook.html}. +#' @source The data used to be available on the www.ssfpack.com/DKbook.html but +#' this page is does not seem to be available anymore. #' @keywords datasets -#' @references James Durbin, Siem Jan Koopman (2012). "Time Series Analysis by State Space Methods". -#' Oxford University Press. +#' @references +#' James Durbin, Siem Jan Koopman (2012). +#' Time Series Analysis by State Space Methods. Oxford University Press. +#' https://doi.org/10.1093/acprof:oso/9780199641178.001.0001 #' @examples +#' \donttest{ # Don't test on CRAN as complains about parallelisation #' data("exchange") #' model <- svm(exchange, rho = uniform(0.97,-0.999,0.999), #' sd_ar = halfnormal(0.175, 2), mu = normal(-0.87, 0, 2)) -#' +#' #' out <- particle_smoother(model, particles = 500) -#' plot.ts(cbind(model$y, exp(out$alphahat))) +#' plot.ts(cbind(model$y, exp(out$alphahat))) +#' } NULL -#' Simulated Poisson time series data +#' Simulated Poisson Time Series Data #' -#' See example for code for reproducing the data. +#' See example for code for reproducing the data. This was used in +#' Vihola, Helske, Franks (2020). #' +#' @srrstats {G5.0, G5.1, G5.4} used in Vihola, Helske, Franks (2020). #' @name poisson_series #' @docType data -#' @format A vector of length 100 +#' @format A vector of length 100. #' @keywords datasets -#' @examples +#' @references +#' Vihola, M, Helske, J, Franks, J (2020). Importance sampling type +#' estimators based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 +#' +#' @examples #' # The data was generated as follows: #' set.seed(321) #' slope <- cumsum(c(0, rnorm(99, sd = 0.01))) #' y <- rpois(100, exp(cumsum(slope + c(0, rnorm(99, sd = 0.1))))) -NULL \ No newline at end of file +NULL +#' +#' Simulated Negative Binomial Time Series Data +#' +#' See example for code for reproducing the data. This was used in +#' Helske and Vihola (2021). +#' + #' @srrstats {G5.1} used in Helske and Vihola (2021). +#' @name negbin_series +#' @docType data +#' @format A time series \code{mts} object with 200 time points and two series. +#' @keywords datasets +#' @seealso \code{negbin_model} +#' @references +#' Helske J, Vihola M (2021). bssm: Bayesian Inference of Non-linear and +#' Non-Gaussian State Space Models in R. The R Journal (2021) 13:2, 578-589. +#' https://doi.org/10.32614/RJ-2021-103 +#' +#' @examples +#' # The data was generated as follows: +#' set.seed(123) +#' n <- 200 +#' sd_level <- 0.1 +#' drift <- 0.01 +#' beta <- -0.9 +#' phi <- 5 +#' +#' level <- cumsum(c(5, drift + rnorm(n - 1, sd = sd_level))) +#' x <- 3 + (1:n) * drift + sin(1:n + runif(n, -1, 1)) +#' y <- rnbinom(n, size = phi, mu = exp(beta * x + level)) +#' +NULL +#' Estimated Negative Binomial Model of Helske and Vihola (2021) +#' +#' This model was used in Helske and Vihola (2021), but with larger number of +#' iterations. Here only 2000 iterations were used in order to reduce the size +#' of the model object in CRAN. +#' +#' @srrstats {G5.0, G5.1, G5.4, BS7.2} used in Helske and Vihola (2021). +#' @name negbin_model +#' @docType data +#' @format A object of class \code{mcmc_output}. +#' @keywords datasets +#' @references +#' Helske J, Vihola M (2021). bssm: Bayesian Inference of Non-linear and +#' Non-Gaussian State Space Models in R. The R Journal (2021) 13:2, 578-589. +#' https://doi.org/10.32614/RJ-2021-103 +#' +#' @examples +#' # reproducing the model: +#' data("negbin_series") +#' # Construct model for bssm +#' bssm_model <- bsm_ng(negbin_series[, "y"], +#' xreg = negbin_series[, "x"], +#' beta = normal(0, 0, 10), +#' phi = halfnormal(1, 10), +#' sd_level = halfnormal(0.1, 1), +#' sd_slope = halfnormal(0.01, 0.1), +#' a1 = c(0, 0), P1 = diag(c(10, 0.1)^2), +#' distribution = "negative binomial") +#' +#' \donttest{ +#' # In the paper we used 60000 iterations with first 10000 as burnin +#' fit_bssm <- run_mcmc(bssm_model, iter = 2000, particles = 10, seed = 1) +#' fit_bssm +#' } +NULL diff --git a/R/check_arguments.R b/R/check_arguments.R index 9c3971f3..dd12ebba 100644 --- a/R/check_arguments.R +++ b/R/check_arguments.R @@ -1,35 +1,102 @@ - +#' Check Arguments +#' +#' @importFrom checkmate test_atomic_vector test_count test_double test_flag +#' test_integerish test_int +#' +#' @param x Variable to be checked. +#' @param name Name of the argument used in printing error messages. +#' @param positive Logical, check for positiveness of \code{x}. +#' @param max Maximum value of \code{x}. +#' @param p An integer, number of time series. +#' @param n An integer, number of time points. +#' @param m An integer, dimensionality of the state vector. +#' @param k An integer, number of predictors. +#' @param multivariate Logical, should \code{p} be larger than 1? +#' @param beta A vector of regression coefficients. +#' @param xreg A matrix or vector of predictors. +#' @param distribution Distribution(s) of the responses. +#' @param y The response time series. +#' @param type Name to be added to the sd parameter name. +#' @param add_prefix Logical, add \code{type} to parameter name. +#' @noRd check_y <- function(x, multivariate = FALSE, distribution = "gaussian") { - - if(multivariate) { - if (!is.matrix(x)) { - stop("Argument y must be a numeric matrix or multivariate ts object.") + if (any(!is.na(x))) { + if (multivariate) { + if (!is.matrix(x)) { + stop("Argument 'y' must be a matrix or multivariate ts object.") + } + if (nrow(x) < 2) { + stop("Number of rows in 'y', i.e. number of time points, must be > 1. ") + } + } else { + if (!is.vector(x) || is.list(x)) { + if (is.ts(x) || is.matrix(x)) { + if (!is.null(dim(x)) && ncol(x) == 1 && length(dim(x)) < 3) { + dim(x) <- NULL + } else { + if(!is.null(dim(x)) && ncol(x) > 1) { + stop("Argument 'y' must be a vector or univariate ts object.") + } + } + } else { + stop("Argument 'y' must be a vector or univariate ts object.") + } + } + if (length(x) < 2) { + stop("Length of argument y, i.e. number of time points, must be > 1.") + } + if (distribution != "gaussian" && any(na.omit(x) < 0)) { + stop(paste0("Negative values not allowed for ", distribution, + " distribution. ")) + } else { + if (distribution %in% + c("negative binomial", "binomial", "poisson") && + any(na.omit(x[is.finite(x)] != as.integer(x[is.finite(x)])))) { + stop(paste0("Non-integer values not allowed for ", distribution, + " distribution. ")) + } + } + } + if (any(is.infinite(x))) { + stop("Argument 'y' must contain only finite or NA values.") } + } + x +} + +check_period <- function(x, n) { + if (!test_int(x)) { + stop("Argument 'period' should be a single integer. ") } else { - if (!(is.vector(x) && !is.list(x)) && !is.numeric(x)) { - stop("Argument y must be a numeric vector or ts object.") + if (x < 3) { + stop("Argument 'period' should be a integer larger than 2. ") } - if(distribution != "gaussian" && any(x < 0)) { - stop(paste0("Negative values not allowed for ", distribution, " distribution. ")) + if (x >= n) { + stop("Period should be less than the number of time points.") } } - if (any(is.infinite(x))) { - stop("Argument y must contain only finite or NA values.") - } - if (length(x) < 2) { - stop("Length of argument y must be at least two.") - } - - + as.integer(x) } - +#' @srrstats {BS2.5} Checks that observations are compatible with their +#' distributions are made. check_distribution <- function(x, distribution) { - for(i in 1:ncol(x)) { - if(distribution[i] != "gaussian" && any(x[,i] < 0)) { - stop(paste0("Negative values not allowed for ", distribution[i], " distribution. ")) + for (i in seq_len(ncol(x))) { + if (distribution[i] != "gaussian" && any(na.omit(x[, i]) < 0)) { + stop(paste0("Negative values not allowed for ", distribution[i], + " distribution. ")) + } else { + if (distribution[i] %in% + c("negative binomial", "binomial", "poisson") && + any(na.omit(x[, i] != as.integer(x[, i])))) { + stop(paste0("Non-integer values not allowed for ", distribution[i], + " distribution. ")) + } } } } + + + check_sd <- function(x, type, add_prefix = TRUE) { if (add_prefix) { @@ -38,7 +105,8 @@ check_sd <- function(x, type, add_prefix = TRUE) { param <- type } if (length(x) != 1) { - stop(paste0("Argument ", param, " must be of length one.")) + stop(paste0("Argument ", param, + " must be of length one (scalar or bssm_prior).")) } if (!is.numeric(x)) { stop(paste0("Argument ", param, " must be numeric.")) @@ -63,10 +131,12 @@ check_xreg <- function(x, n) { } + check_beta <- function(x, k) { - + if(!is.numeric(x)) stop("'beta' must be numeric. ") if (length(x) != k) { - stop("Number of coefficients in beta is not equal to the number of columns of xreg.") + stop(paste("Number of coefficients in beta is not equal to the number", + "of columns of xreg.", sep = " ")) } if (any(!is.finite(x))) { stop("Argument 'beta' must contain only finite values. ") @@ -84,6 +154,7 @@ check_mu <- function(x) { } } + check_rho <- function(x) { if (length(x) != 1) { @@ -94,54 +165,291 @@ check_rho <- function(x) { } } -check_phi <- function(x, distribution) { + + +check_phi <- function(x) { if (x < 0) { stop("Parameter 'phi' must be non-negative.") } } -check_u <- function(x, multivariate = FALSE) { + +check_u <- function(x, y, multivariate = FALSE) { if (any(x < 0)) { stop("All values of 'u' must be non-negative.") } - if(multivariate) { + if (multivariate) { + if (length(x) == 1) x <- matrix(x, nrow(y), ncol(y)) + if (!is.matrix(x) && !is.numeric(x)) { stop("Argument 'u' must be a numeric matrix or multivariate ts object.") } + if(!identical(dim(y), dim(x))) + stop("Dimensions of 'y' and 'u' do not match. ") } else { + if (length(x) == 1) x <- rep(x, length(y)) if (!(is.vector(x) && !is.list(x)) && !is.numeric(x)) { stop("Argument 'u' must be a numeric vector or ts object.") } + if (length(x) != length(y)) + stop("Lengths of 'u' and 'y' do not match.") + dim(x) <- NULL } if (any(is.infinite(x))) { stop("Argument 'u' must contain only finite values.") } + x } + check_prior <- function(x, name) { if (!is_prior(x) && !is_prior_list(x)) { stop(paste(name, "must be of class 'bssm_prior' or 'bssm_prior_list'.")) } } -check_target <- function(target) { - if(length(target) > 1 || target >= 1 || target <= 0) { - stop("Argument 'target' must be on interval (0, 1).") +check_prop <- function(x, name = "target") { + if (length(x) > 1 || x >= 1 || x <= 0) { + stop(paste0("Argument '", name, "' must be on interval (0, 1).")) } } check_D <- function(x, p, n) { - if(p == 1) { - if (!(length(x) %in% c(1,n))) { - stop("'D' must be a scalar or length n, where n is the number of observations.") + if (missing(x) || is.null(x)) { + x <- if (p == 1) 0 else matrix(0, p, 1) + } else { + if(!is.numeric(x)) stop("'D' must be numeric. ") + if (p == 1) { + if (!(length(x) %in% c(1, n))) { + stop(paste("'D' must be a scalar or length n, where n is the number of", + "observations.", sep = " ")) + x <- as.numeric(x) + } + } else { + if (is.null(dim(x)) || nrow(x) != p || !(ncol(x) %in% c(1, n))) { + stop(paste("'D' must be p x 1 or p x n matrix, where p is the number", + "of series.", sep = " ")) + } + } + } + x +} + +check_C <- function(x, m, n) { + if (missing(x) || is.null(x)) { + x <- matrix(0, m, 1) + } else { + if(!is.numeric(x)) stop("'C' must be numeric. ") + if (is.null(dim(x)) || nrow(x) != m || !(ncol(x) %in% c(1, n))) { + stop(paste("'C' must be m x 1 or m x n matrix, where m is", + "the number of states.", sep = " ")) } + } + x +} + + + + + +create_regression <- function(beta, xreg, n) { + if (missing(xreg) || is.null(xreg)) { + list(xreg = matrix(0, 0, 0), coefs = numeric(0), beta = NULL) } else { - if (is.null(dim(x)) || nrow(x) != p || !(ncol(x) %in% c(1,n))) { - stop("'D' must be p x 1 or p x n matrix, where p is the number of series.") + if (missing(beta) || is.null(beta)) { + stop("No prior defined for beta. ") + } else { + if (!is_prior(beta) && !is_prior_list(beta)) { + stop(paste("Prior for beta must be of class 'bssm_prior' or", + "'bssm_prior_list.", sep = " " )) + } else { + if (is.null(dim(xreg))) { + if (length(xreg) == n) { + dim(xreg) <- c(n, 1) + } else { + stop("Length of xreg is not equal to the length of the series y.") + } + } + check_xreg(xreg, n) + nx <- ncol(xreg) + if (nx == 1 && is_prior_list(beta)) beta <- beta[[1]] + if (nx > 1) { + coefs <- vapply(beta, "[[", "init", FUN.VALUE = 1) + } else { + coefs <- beta$init + } + check_beta(coefs, nx) + if (nx > 0 && is.null(colnames(xreg))) { + colnames(xreg) <- paste0("coef_", seq_len(ncol(xreg))) + } + names(coefs) <- colnames(xreg) + } + } + list(xreg = xreg, coefs = coefs, beta = beta) + } +} + +check_Z <- function(x, p, n, multivariate = FALSE) { + if(!is.numeric(x)) stop("'Z' must be numeric. ") + if (!multivariate) { + if (length(x) == 1) { + dim(x) <- c(1, 1) + } else { + if (!(dim(x)[2] %in% c(1, NA, n))) { + stop(paste("'Z' must be a (m x 1) or (m x n) matrix, where", + "m is the number of states and n is the length of the series. ", + sep = " ")) + } else { + dim(x) <- + c(dim(x)[1], (n - 1) * (max(dim(x)[2], 0, na.rm = TRUE) > 1) + 1) + } } + } else { + if(p == 1 && length(x) == 1) { + dim(x) <- c(1, 1, 1) + } else { + if (is.null(dim(x)) || dim(x)[1] != p || !(dim(x)[3] %in% c(1, NA, n))) { + stop(paste("'Z' must be a (p x m) matrix or (p x m x n) array", + "where p is the number of series, m is the number of states,", + "and n is the length of the series. ", sep = " ")) + } + } + dim(x) <- + c(p, dim(x)[2], (n - 1) * (max(dim(x)[3], 0, na.rm = TRUE) > 1) + 1) } + x } -check_C <- function(x, m, n) { - if (is.null(dim(x)) || nrow(x) != m || !(ncol(x) %in% c(1,n))) { - stop("'C' must be m x 1 or m x n matrix, where m is the number of states.") - } +check_T <- function(x, m, n) { + if(!is.numeric(x)) stop("'T' must be numeric. ") + if (length(x) == 1 && m == 1) { + dim(x) <- c(1, 1, 1) + } else { + if ((length(x) == 1) || any(dim(x)[1:2] != m) || + !(dim(x)[3] %in% c(1, NA, n))) { + stop(paste("'T' must be a (m x m) matrix, (m x m x 1) or", + "(m x m x n) array, where m is the number of states. ", sep = " ")) + } + dim(x) <- c(m, m, (n - 1) * (max(dim(x)[3], 0, na.rm = TRUE) > 1) + 1) + } + x +} + +check_R <- function(x, m, n) { + if (length(x) == m) { + dim(x) <- c(m, 1, 1) + } else { + if(!is.numeric(x)) stop("'R' must be numeric. ") + if (!(dim(x)[1] == m) || dim(x)[2] > m || !dim(x)[3] %in% c(1, NA, n)) { + stop(paste("'R' must be a (m x k) matrix, (m x k x 1) or", + "(m x k x n) array, where k<=m is the number of disturbances eta,", + "and m is the number of states. ", sep = " ")) + } else { + dim(x) <- + c(m, dim(x)[2], (n - 1) * (max(dim(x)[3], 0, na.rm = TRUE) > 1) + 1) + } + } + x +} + +check_a1 <- function(x, m) { + if (missing(x) || is.null(x)) { + x <- numeric(m) + } else { + if(!is.numeric(x)) stop("'a1' must be numeric. ") + if (length(x) == 1 || length(x) == m) { + x <- rep(x, length.out = m) + } else { + stop(paste("Misspecified a1, argument a1 must be a vector of length m,", + "where m is the number of state_names and 1<=t<=m.", sep = " ")) + } + } + x +} + + +check_P1 <- function(x, m) { + if (missing(x) || is.null(x)) { + x <- matrix(0, m, m) + } else { + if(!is.numeric(x)) stop("'P1' must be numeric. ") + if (length(x) == 1 && m == 1) { + dim(x) <- c(1, 1) + } else { + if (!identical(dim(x), c(m, m))) + stop(paste("Argument P1 must be (m x m) matrix, where m is the number", + "of states. ", sep = " ")) + } + } + x +} + + +check_H <- function(x, p, n, multivariate = FALSE) { + + if(!is.numeric(x)) stop("'H' must be numeric. ") + + if (!multivariate) { + if (!(length(x) %in% c(1, n))) { + stop(paste("'H' must be a scalar or length n, where n is the length of", + "the time series y", sep = " ")) + } else x <- as.numeric(x) + } else { + if (any(dim(x)[1:2] != p) || !(dim(x)[3] %in% c(1, n, NA))) { + stop(paste("'H' must be p x p matrix or p x p x n array, where p is the", + "number of series and n is the length of the series.", sep = " ")) + } else { + dim(x) <- c(p, p, (n - 1) * (max(dim(x)[3], 0, na.rm = TRUE) > 1) + 1) + } + } + x +} + + +check_intmax <- function(x, name = "particles", positive = TRUE, max = 1e5) { + # autotest complains without additional positivity test + if (!test_count(x, positive) | (positive & x <= 0)) { + stop(paste0("Argument '", name, "' should be a ", + ifelse(positive, "positive", "non-negative"), " integer. ")) + } + if (x > max) { + stop(paste0("You probably do not want '", name, "' > ", max, + ". If you really do, please file an issue at Github. ")) + } + as.integer(x) +} + +check_positive_real <- function(x, name) { + if (!test_double(x, lower=0, finite = TRUE, any.missing = FALSE, len = 1)) { + stop(paste0("Argument '", name, "' should be positive real value.")) + } + x +} + +check_theta <- function(x) { + + if (!is.numeric(x) || !test_atomic_vector(x)) { + stop("Argument 'theta' should be a numeric vector.") + } + if (is.null(names(x))) { + names(x) <- paste("theta_", seq_len(length(x))) + } + x +} + +check_missingness <- function(x) { + if (!inherits(x, c("ssm_nlg", "ssm_sde"))) { + if (is.null(x$prior_parameters)) { + contains_na <- + anyNA(x[-which(names(x) %in% c("y", "update_fn", "prior_fn"))], + recursive = TRUE) + if (contains_na) stop(paste( + "Missing values not allowed in the model object", + "(except in component 'y').")) + } else { + contains_na <- anyNA(x[-which(names(x) %in% c("y", "prior_parameters"))], + recursive = TRUE) + if (contains_na) stop(paste( + "Missing values not allowed in the model object", + "(except in components 'y' and 'prior_parameters').")) + } + } } diff --git a/R/check_diagnostics.R b/R/check_diagnostics.R new file mode 100644 index 00000000..6aeef140 --- /dev/null +++ b/R/check_diagnostics.R @@ -0,0 +1,91 @@ +#' Quick Diagnostics Checks for \code{run_mcmc} Output +#' +#' Prints out the acceptance rate, smallest effective sample sizes (ESS) and +#' largest Rhat values for a quick first check that the sampling worked. For +#' further checks, see e.g. \code{bayesplot} and \code{coda} packages. +#' +#' For methods other than IS-MCMC, the estimates are based on the improved +#' diagnostics from the \code{posterior} package.For IS-MCMC, these Rhat, +#' bulk-ESS, and tail-ESS estimates are based on the approximate posterior +#' which should look reasonable, otherwise the IS-correction does not make much +#' sense. For IS-MCMC, ESS estimates based on a weighted posterior are also +#' computed. +#' +#' @importFrom posterior summarise_draws default_convergence_measures +#' @param x Results object of class \code{mcmc_output} from +#' \code{\link{run_mcmc}}. +#' @export +#' @srrstats {BS5.3, BS5.5} Several options for ESS. See also asymptotic_var.R +#' and summary functions +#' @examples +#' set.seed(1) +#' n <- 30 +#' phi <- 2 +#' rho <- 0.9 +#' sigma <- 0.1 +#' beta <- 0.5 +#' u <- rexp(n, 0.1) +#' x <- rnorm(n) +#' z <- y <- numeric(n) +#' z[1] <- rnorm(1, 0, sigma / sqrt(1 - rho^2)) +#' y[1] <- rnbinom(1, mu = u * exp(beta * x[1] + z[1]), size = phi) +#' for(i in 2:n) { +#' z[i] <- rnorm(1, rho * z[i - 1], sigma) +#' y[i] <- rnbinom(1, mu = u * exp(beta * x[i] + z[i]), size = phi) +#' } +#' +#' model <- ar1_ng(y, rho = uniform_prior(0.9, 0, 1), +#' sigma = gamma_prior(0.1, 2, 10), mu = 0., +#' phi = gamma_prior(2, 2, 1), distribution = "negative binomial", +#' xreg = x, beta = normal_prior(0.5, 0, 1), u = u) +#' +#' out <- run_mcmc(model, iter = 1000, particles = 10) +#' check_diagnostics(out) +check_diagnostics <- function(x) { + + cat("\nAcceptance rate after the burn-in period: ", + paste(round(x$acceptance_rate, 3), "\n", sep = "")) + + cat("\nRun time (wall-clock):\n") + cat(paste(ifelse(x$time[3] < 10, round(x$time[3], 2), round(x$time[3])), + "seconds.\n")) + + if (any(is.na(x$theta)) || any(is.na(x$alpha))) { + warning("NA value found in samples.") + } + draws <- suppressWarnings(as_draws(x)) + + is_run <- x$mcmc_type %in% paste0("is", 1:3) + if (is_run) { + # removing hidden variables of draws object gives warning, we don't care + ess <- apply(suppressWarnings(draws[, 2:(ncol(draws) - 3)]), + 2, function(x) { + weighted_var(x, draws$weight) / asymptotic_var(x, draws$weight) + }) + min_ess <- which.min(ess) + cat("\nSmallest ESS based on weighted posterior: ", + round(ess[min_ess]), " (", names(ess)[min_ess], ")", sep = "") + + ess_is <- apply(suppressWarnings(draws[, 2:(ncol(draws) - 3)]), 2, + function(x) ess(draws$weight, identity, x)) + min_ess <- which.min(ess_is) + cat("\nSmallest ESS based on independent importance sampling: ", + round(ess[min_ess]), " (", names(ess_is)[min_ess], ")", sep = "") + + cat("\n\nNote: The input is based on a IS-weighted MCMC, so the ", + "approximate (non-weighted) posterior is used when computing the Rhat ", + "and ESS measures below.\n", sep="") + } + + sumr <- summarise_draws(draws, default_convergence_measures()) + min_ess <- which.min(sumr$ess_bulk) + cat("\nSmallest bulk-ESS: ", round(sumr$ess_bulk[min_ess]), " (", + sumr$variable[min_ess], ")", sep = "") + min_ess <- which.min(sumr$ess_tail) + cat("\nSmallest tail-ESS: ", round(sumr$ess_tail[min_ess]), " (", + sumr$variable[min_ess], ")", sep = "") + max_rhat <- which.max(sumr$rhat) + cat("\nLargest Rhat: ", round(sumr$rhat[max_rhat], 3), " (", + sumr$variable[max_rhat], ")", sep = "") + invisible(x) +} diff --git a/R/cpp_example_models.R b/R/cpp_example_models.R new file mode 100644 index 00000000..c3ede175 --- /dev/null +++ b/R/cpp_example_models.R @@ -0,0 +1,765 @@ +#' Example C++ Codes for Non-Linear and SDE Models +#' +#' @param example Name of the example model. +#' Run \code{cpp_example_model("abc")} to get the names of possible models. +#' @param return_code If TRUE, will not compile the model but only returns the +#' corresponding code. +#' @return Returns pointers to the C++ snippets defining the model, or in case +#' of \code{return_code = TRUE}, returns the example code without compiling. +#' @export +#' @srrstats {G5.4} sde_gbm model used in Vihola, Helske, Franks, (2020). See +#' also tests/testthat/test_sde.R. +#' @examples +#' cpp_example_model("sde_poisson_OU", return_code = TRUE) +#' +cpp_example_model <- function(example, return_code = FALSE) { + + example <- match.arg(tolower(example), c("nlg_linear_gaussian", + "nlg_sin_exp", "nlg_growth", "nlg_ar_exp", "sde_poisson_ou", "sde_gbm")) + + if (!test_flag(return_code)) + stop("Argument 'return_code' should be TRUE or FALSE. ") + + code <- switch(example, + "sde_poisson_ou" = { + ' + // A latent Ornstein-Uhlenbeck process with Poisson observations + // dalpha_t = rho (nu - alpha_t) dt + sigma dB_t, t>=0 + // y_k ~ Poisson(exp(alpha_k)), k = 1,...,n + + #include + // [[Rcpp::depends(RcppArmadillo)]] + // [[Rcpp::interfaces(r, cpp)]] + + // x: state + // theta: vector of parameters + + // theta(0) = log_rho + // theta(1) = nu + // theta(2) = log_sigma + + // Drift function + // [[Rcpp::export]] + double drift(const double x, const arma::vec& theta) { + return exp(theta(0)) * (theta(1) - x); + } + // diffusion function + // [[Rcpp::export]] + double diffusion(const double x, const arma::vec& theta) { + return exp(theta(2)); + } + // Derivative of the diffusion function + // [[Rcpp::export]] + double ddiffusion(const double x, const arma::vec& theta) { + return 0.0; + } + + // log-density of the prior + // [[Rcpp::export]] + double log_prior_pdf(const arma::vec& theta) { + + // rho ~ gamma(2, 0.5) // shape-scale parameterization + // nu ~ N(0, 4) + // sigma ~ half-N(0,1) (theta(2) is log(sigma)) + double log_pdf = + R::dgamma(exp(theta(0)), 2, 0.5, 1) + + R::dnorm(theta(1), 0, 4, 1) + + R::dnorm(exp(theta(2)), 0, 1, 1) + + theta(0) + theta(2); // jacobians of transformations + return log_pdf; + } + + // log-density of observations + // given vector of sampled states alpha + // [[Rcpp::export]] + arma::vec log_obs_density(const double y, + const arma::vec& alpha, const arma::vec& theta) { + + arma::vec log_pdf(alpha.n_elem); + for (unsigned int i = 0; i < alpha.n_elem; i++) { + log_pdf(i) = R::dpois(y, exp(alpha(i)), 1); + } + return log_pdf; + } + + // Function returning the pointers to above functions (no need to modify) + + // [[Rcpp::export]] + Rcpp::List create_xptrs() { + // typedef for a pointer of drift/volatility function + typedef double (*fnPtr)(const double x, const arma::vec& theta); + // typedef for log_prior_pdf + typedef double (*prior_fnPtr)(const arma::vec& theta); + // typedef for log_obs_density + typedef arma::vec (*obs_fnPtr)(const double y, + const arma::vec& alpha, const arma::vec& theta); + + return Rcpp::List::create( + Rcpp::Named("drift") = Rcpp::XPtr(new fnPtr(&drift)), + Rcpp::Named("diffusion") = Rcpp::XPtr(new fnPtr(&diffusion)), + Rcpp::Named("ddiffusion") = + Rcpp::XPtr(new fnPtr(&ddiffusion)), + Rcpp::Named("prior") = + Rcpp::XPtr(new prior_fnPtr(&log_prior_pdf)), + Rcpp::Named("obs_density") = + Rcpp::XPtr(new obs_fnPtr(&log_obs_density))); + } + ' + }, + "sde_gbm" = { + ' + // A latent Geometric Brownian motion with Gaussian observations + // dx_t = mu * x_t * dt + sigma_x * x_t * dB_t, t>=0, + // y_k ~ N(log(x_k), sigma_y^2), k = 1,...,n + // See Vihola, Helske, and Franks (2020) + + // x: state + // theta: vector of parameters + + // theta(0) = mu + // theta(1) = sigma_x + // theta(2) = sigma_y + + #include + // [[Rcpp::depends(RcppArmadillo)]] + // [[Rcpp::interfaces(r, cpp)]] + + // Drift function + // [[Rcpp::export]] + double drift(const double x, const arma::vec& theta) { + return theta(0) * x; + } + // diffusion function + // [[Rcpp::export]] + double diffusion(const double x, const arma::vec& theta) { + return std::max(0.0, theta(1) * x); + } + // Derivative of the diffusion function + // [[Rcpp::export]] + double ddiffusion(const double x, const arma::vec& theta) { + return theta(1) * (x > 0.0); + } + + // log-density of the prior + // [[Rcpp::export]] + double log_prior_pdf(const arma::vec& theta) { + + double log_pdf = 0.0; + + if(theta(0) < 0 || theta(1) < 0 || theta(2) < 0.5) { + log_pdf = -std::numeric_limits::infinity(); + } + else { + log_pdf = R::dnorm(theta(0), 0, 0.1, 1) + + R::dnorm(theta(1), 0, 0.5, 1) + + R::dnorm(theta(2), 1.5, 0.5, 1); + } + return log_pdf; + } + + // log-density of observations + // given vector of sampled states alpha + // [[Rcpp::export]] + arma::vec log_obs_density(const double y, + const arma::vec& alpha, const arma::vec& theta) { + + arma::vec log_pdf(alpha.n_elem); + for (unsigned int i = 0; i < alpha.n_elem; i++) { + log_pdf(i) = R::dnorm(y, log(alpha(i)), theta(2), 1); + } + return log_pdf; + } + + // [[Rcpp::export]] + Rcpp::List create_xptrs() { + // typedef for a pointer of drift/volatility function + typedef double (*fnPtr)(const double x, const arma::vec& theta); + // typedef for log_prior_pdf + typedef double (*prior_fnPtr)(const arma::vec& theta); + // typedef for log_obs_density + typedef arma::vec (*obs_fnPtr)(const double y, + const arma::vec& alpha, const arma::vec& theta); + + return Rcpp::List::create( + Rcpp::Named("drift") = Rcpp::XPtr(new fnPtr(&drift)), + Rcpp::Named("diffusion") = Rcpp::XPtr(new fnPtr(&diffusion)), + Rcpp::Named("ddiffusion") = Rcpp::XPtr(new fnPtr(&ddiffusion)), + Rcpp::Named("prior") = + Rcpp::XPtr(new prior_fnPtr(&log_prior_pdf)), + Rcpp::Named("obs_density") = + Rcpp::XPtr(new obs_fnPtr(&log_obs_density))); + } + ' + }, + "nlg_ar_exp" = { + ' + // alpha_t+1 = (1-rho)mu + rho * alpha_t + eta_t, eta_t ~ N(0, sigma_x^2) + // y_t ~ N(exp(alpha_t), sigma_y^2) + + // theta(0) = mu + // theta(1) = rho + // theta(2) = sigma_x + // theta(3) = sigma_y + + #include + // [[Rcpp::depends(RcppArmadillo)]] + // [[Rcpp::interfaces(r, cpp)]] + + // Function for the prior mean of alpha_1 + // [[Rcpp::export]] + arma::vec a1_fn(const arma::vec& theta, const arma::vec& known_params) { + + arma::vec a1(1); + a1(0) = theta(0); + return a1; + } + // Function for the prior covariance matrix of alpha_1 + // [[Rcpp::export]] + arma::mat P1_fn(const arma::vec& theta, const arma::vec& known_params) { + + arma::mat P1(1,1); + P1(0,0) = pow(exp(theta(2)), 2) / (1 - pow(theta(1), 2)); + return P1; + } + + // Function for the observational level standard deviation + // [[Rcpp::export]] + arma::mat H_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat H(1,1); + H(0, 0) = exp(theta(3)); + return H; + } + + // Function for the Cholesky of state level covariance + // [[Rcpp::export]] + arma::mat R_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat R(1, 1); + R(0, 0) = exp(theta(2)); + return R; + } + + + // Z function + // [[Rcpp::export]] + arma::vec Z_fn(const unsigned int t, const arma::vec& alpha, const + arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + return exp(alpha); + } + // Jacobian of Z function + // [[Rcpp::export]] + arma::mat Z_gn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat Z_gn(1, 1); + Z_gn(0, 0) = exp(alpha(0)); + return Z_gn; + } + + // T function + // [[Rcpp::export]] + arma::vec T_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + return theta(0) * (1 - theta(1)) + theta(1) * alpha; + } + + // Jacobian of T function + // [[Rcpp::export]] + arma::mat T_gn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat Tg(1, 1); + Tg(0, 0) = theta(1); + + return Tg; + } + + // log-prior pdf for theta + // [[Rcpp::export]] + double log_prior_pdf(const arma::vec& theta) { + + double log_pdf = + R::dnorm(theta(0), 0, 10, 1) + // N(0,10) for mu + R::dbeta(theta(1), 2, 2, 1) + // beta(2, 2) for rho + R::dnorm(exp(theta(2)), 0, 1, 1) + theta(2) + + R::dnorm(exp(theta(3)), 0, 1, 1) + theta(3);//half-N(0, 1) for sigmas + + return log_pdf; + } + + // [[Rcpp::export]] + Rcpp::List create_xptrs() { + // typedef for a pointer of nonlinear function returning vec + typedef arma::vec (*vec_fnPtr)(const unsigned int t, + const arma::vec& alpha, const arma::vec& theta, + const arma::vec& known_params, const arma::mat& known_tv_params); + // for a pointer of nonlinear function of model equation returning mat + typedef arma::mat (*mat_fnPtr)(const unsigned int t, + const arma::vec& alpha, const arma::vec& theta, + const arma::vec& known_params, const arma::mat& known_tv_params); + // for a pointer of nonlinear function of model equation returning vec + typedef arma::vec (*vec_initfnPtr)(const arma::vec& theta, + const arma::vec& known_params); + // for a pointer of nonlinear function of model equation returning mat + typedef arma::mat (*mat_initfnPtr)(const arma::vec& theta, + const arma::vec& known_params); + // typedef for a pointer of log-prior function + typedef double (*double_fnPtr)(const arma::vec&); + + return Rcpp::List::create( + Rcpp::Named("a1_fn") = + Rcpp::XPtr(new vec_initfnPtr(&a1_fn)), + Rcpp::Named("P1_fn") = + Rcpp::XPtr(new mat_initfnPtr(&P1_fn)), + Rcpp::Named("Z_fn") = Rcpp::XPtr(new vec_fnPtr(&Z_fn)), + Rcpp::Named("H_fn") = Rcpp::XPtr(new mat_fnPtr(&H_fn)), + Rcpp::Named("T_fn") = Rcpp::XPtr(new vec_fnPtr(&T_fn)), + Rcpp::Named("R_fn") = Rcpp::XPtr(new mat_fnPtr(&R_fn)), + Rcpp::Named("Z_gn") = Rcpp::XPtr(new mat_fnPtr(&Z_gn)), + Rcpp::Named("T_gn") = Rcpp::XPtr(new mat_fnPtr(&T_gn)), + Rcpp::Named("log_prior_pdf") = + Rcpp::XPtr(new double_fnPtr(&log_prior_pdf))); + } + ' + }, + "nlg_growth" = { + ' + //univariate growth model (see vignette growth_model) + + #include + // [[Rcpp::depends(RcppArmadillo)]] + // [[Rcpp::interfaces(r, cpp)]] + + // Unknown parameters theta: + // theta(0) = log(H) + // theta(1) = log(R_1) + // theta(2) = log(R_2) + + // Function for the prior mean of alpha_1 + // [[Rcpp::export]] + arma::vec a1_fn(const arma::vec& theta, const arma::vec& known_params) { + + arma::vec a1(2); + a1(0) = known_params(2); + a1(1) = known_params(3); + return a1; + } + // Function for the prior covariance matrix of alpha_1 + // [[Rcpp::export]] + arma::mat P1_fn(const arma::vec& theta, const arma::vec& known_params) { + + arma::mat P1(2, 2, arma::fill::zeros); + P1(0,0) = known_params(4); + P1(1,1) = known_params(5); + return P1; + } + + // Function for the observational level standard deviation + // [[Rcpp::export]] + arma::mat H_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat H(1,1); + H(0, 0) = exp(theta(0)); + return H; + } + + // Function for the Cholesky of state level covariance + // [[Rcpp::export]] + arma::mat R_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat R(2, 2, arma::fill::zeros); + R(0, 0) = exp(theta(1)); + R(1, 1) = exp(theta(2)); + return R; + } + + // Z function + // [[Rcpp::export]] + arma::vec Z_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::vec tmp(1); + tmp(0) = alpha(1); + return tmp; + } + // Jacobian of Z function + // [[Rcpp::export]] + arma::mat Z_gn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat Z_gn(1, 2); + Z_gn(0, 0) = 0.0; + Z_gn(0, 1) = 1.0; + return Z_gn; + } + + // T function + // [[Rcpp::export]] + arma::vec T_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + double dT = known_params(0); + double K = known_params(1); + + arma::vec alpha_new(2); + alpha_new(0) = alpha(0); + double r = exp(alpha(0)) / (1.0 + exp(alpha(0))); + alpha_new(1) = K * alpha(1) * exp(r * dT) / + (K + alpha(1) * (exp(r * dT) - 1)); + return alpha_new; + } + + // Jacobian of T function + // [[Rcpp::export]] + arma::mat T_gn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + double dT = known_params(0); + double K = known_params(1); + double r = exp(alpha(0)) / (1 + exp(alpha(0))); + double tmp = + exp(r * dT) / std::pow(K + alpha(1) * (exp(r * dT) - 1), 2); + + arma::mat Tg(2, 2); + Tg(0, 0) = 1.0; + Tg(0, 1) = 0; + Tg(1, 0) = + dT * K * alpha(1) * (K - alpha(1)) * tmp * r / (1 + exp(alpha(0))); + Tg(1, 1) = K * K * tmp; + + return Tg; + } + + // log-prior pdf for theta + // [[Rcpp::export]] + double log_prior_pdf(const arma::vec& theta) { + + // weakly informative half-N(0, 4) priors. + // Note that the sampling is on log-scale, + // so we need to add jacobians of the corresponding transformations + // we could also sample on natural scale with check such as + // if(arma::any(theta < 0)) + // return -std::numeric_limits::infinity(); + // but this would be less efficient. + + // You can use R::dnorm and similar functions, see, e.g. + // https://teuder.github.io/rcpp4everyone_en/220_dpqr_functions.html + double log_pdf = + R::dnorm(exp(theta(0)), 0, 2, 1) + + R::dnorm(exp(theta(1)), 0, 2, 1) + + R::dnorm(exp(theta(2)), 0, 2, 1) + + arma::accu(theta); //jacobian term + + return log_pdf; + } + + + // [[Rcpp::export]] + Rcpp::List create_xptrs() { + + // typedef for a pointer of nonlinear function returning vec (T, Z) + typedef arma::vec (*nvec_fnPtr)(const unsigned int t, + const arma::vec& alpha, const arma::vec& theta, + const arma::vec& known_params, const arma::mat& known_tv_params); + // for a pointer of nonlinear function returning mat (Tg, Zg, H, R) + typedef arma::mat (*nmat_fnPtr)(const unsigned int t, + const arma::vec& alpha, const arma::vec& theta, + const arma::vec& known_params, const arma::mat& known_tv_params); + + // typedef for a pointer returning a1 + typedef arma::vec (*a1_fnPtr)(const arma::vec& theta, + const arma::vec& known_params); + // typedef for a pointer returning P1 + typedef arma::mat (*P1_fnPtr)(const arma::vec& theta, + const arma::vec& known_params); + // typedef for a pointer of log-prior function + typedef double (*prior_fnPtr)(const arma::vec& theta); + + return Rcpp::List::create( + Rcpp::Named("a1_fn") = Rcpp::XPtr(new a1_fnPtr(&a1_fn)), + Rcpp::Named("P1_fn") = Rcpp::XPtr(new P1_fnPtr(&P1_fn)), + Rcpp::Named("Z_fn") = Rcpp::XPtr(new nvec_fnPtr(&Z_fn)), + Rcpp::Named("H_fn") = Rcpp::XPtr(new nmat_fnPtr(&H_fn)), + Rcpp::Named("T_fn") = Rcpp::XPtr(new nvec_fnPtr(&T_fn)), + Rcpp::Named("R_fn") = Rcpp::XPtr(new nmat_fnPtr(&R_fn)), + Rcpp::Named("Z_gn") = Rcpp::XPtr(new nmat_fnPtr(&Z_gn)), + Rcpp::Named("T_gn") = Rcpp::XPtr(new nmat_fnPtr(&T_gn)), + Rcpp::Named("log_prior_pdf") = + Rcpp::XPtr(new prior_fnPtr(&log_prior_pdf))); + + } + ' + }, + "nlg_linear_gaussian" = { + ' + #include + // [[Rcpp::depends(RcppArmadillo)]] + // [[Rcpp::interfaces(r, cpp)]] + + // [[Rcpp::export]] + arma::vec a1_fn(const arma::vec& theta, const arma::vec& known_params) { + + arma::vec a1(1); + a1(0) = 0; + return a1; + } + // [[Rcpp::export]] + arma::mat P1_fn(const arma::vec& theta, const arma::vec& known_params) { + + arma::mat P1(1, 1); + P1(0,0) = 1; + return P1; + } + + // [[Rcpp::export]] + arma::mat H_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat H(1,1); + H(0, 0) = exp(theta(0)); + return H; + } + + // Function for the Cholesky of state level covariance + // [[Rcpp::export]] + arma::mat R_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat R(1, 1); + R(0, 0) = 1; + return R; + } + + // Z function + // [[Rcpp::export]] + arma::vec Z_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + return alpha; + } + // Jacobian of Z function + // [[Rcpp::export]] + arma::mat Z_gn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat Z_gn(1, 1); + Z_gn(0, 0) = 1.0; + return Z_gn; + } + + // T function + // [[Rcpp::export]] + arma::vec T_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + return alpha; + } + + // Jacobian of T function + // [[Rcpp::export]] + arma::mat T_gn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat Tg(1, 1); + Tg(0, 0) = 1.0; + return Tg; + } + + // log-prior pdf for theta + // [[Rcpp::export]] + double log_prior_pdf(const arma::vec& theta) { + return R::dnorm(exp(theta(0)), 0, 1, 1) + theta(0); //jacobian term + } + + // [[Rcpp::export]] + Rcpp::List create_xptrs() { + + // typedef for a pointer of nonlinear function of model equation + // returning vec (T, Z) + typedef arma::vec (*nvec_fnPtr)(const unsigned int t, + const arma::vec& alpha, const arma::vec& theta, + const arma::vec& known_params, const arma::mat& known_tv_params); + // typedef for a pointer of nonlinear fn returning mat (Tg, Zg, H, R) + typedef arma::mat (*nmat_fnPtr)(const unsigned int t, + const arma::vec& alpha, const arma::vec& theta, + const arma::vec& known_params, const arma::mat& known_tv_params); + + // typedef for a pointer returning a1 + typedef arma::vec (*a1_fnPtr)(const arma::vec& theta, + const arma::vec& known_params); + // typedef for a pointer returning P1 + typedef arma::mat (*P1_fnPtr)(const arma::vec& theta, + const arma::vec& known_params); + // typedef for a pointer of log-prior function + typedef double (*prior_fnPtr)(const arma::vec& theta); + + return Rcpp::List::create( + Rcpp::Named("a1_fn") = Rcpp::XPtr(new a1_fnPtr(&a1_fn)), + Rcpp::Named("P1_fn") = Rcpp::XPtr(new P1_fnPtr(&P1_fn)), + Rcpp::Named("Z_fn") = Rcpp::XPtr(new nvec_fnPtr(&Z_fn)), + Rcpp::Named("H_fn") = Rcpp::XPtr(new nmat_fnPtr(&H_fn)), + Rcpp::Named("T_fn") = Rcpp::XPtr(new nvec_fnPtr(&T_fn)), + Rcpp::Named("R_fn") = Rcpp::XPtr(new nmat_fnPtr(&R_fn)), + Rcpp::Named("Z_gn") = Rcpp::XPtr(new nmat_fnPtr(&Z_gn)), + Rcpp::Named("T_gn") = Rcpp::XPtr(new nmat_fnPtr(&T_gn)), + Rcpp::Named("log_prior_pdf") = + Rcpp::XPtr(new prior_fnPtr(&log_prior_pdf))); + } + ' + }, + "nlg_sin_exp" = { + ' + #include + // [[Rcpp::depends(RcppArmadillo)]] + // [[Rcpp::interfaces(r, cpp)]] + + // [[Rcpp::export]] + arma::vec a1_fn(const arma::vec& theta, const arma::vec& known_params) { + + arma::vec a1(1); + a1(0) = 0; + return a1; + } + // [[Rcpp::export]] + arma::mat P1_fn(const arma::vec& theta, const arma::vec& known_params) { + + arma::mat P1(1, 1); + P1(0,0) = 1; + return P1; + } + + // [[Rcpp::export]] + arma::mat H_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat H(1,1); + H(0, 0) = exp(theta(0)); + return H; + } + + // Function for the Cholesky of state level covariance + // [[Rcpp::export]] + arma::mat R_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat R(1, 1); + R(0, 0) = exp(theta(1)); + return R; + } + + // Z function + // [[Rcpp::export]] + arma::vec Z_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + return exp(alpha); + } + // Jacobian of Z function + // [[Rcpp::export]] + arma::mat Z_gn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat Z_gn(1, 1); + Z_gn(0, 0) = exp(alpha(0)); + return Z_gn; + } + + // T function + // [[Rcpp::export]] + arma::vec T_fn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + return sin(alpha); + } + + // Jacobian of T function + // [[Rcpp::export]] + arma::mat T_gn(const unsigned int t, const arma::vec& alpha, + const arma::vec& theta, const arma::vec& known_params, + const arma::mat& known_tv_params) { + + arma::mat Tg(1, 1); + Tg(0, 0) = cos(alpha(0)); + return Tg; + } + + // log-prior pdf for theta + // [[Rcpp::export]] + double log_prior_pdf(const arma::vec& theta) { + return R::dnorm(exp(theta(0)), 0, 1, 1) + theta(0) + + R::dnorm(exp(theta(1)), 0, 1, 1) + theta(1); + } + + // [[Rcpp::export]] + Rcpp::List create_xptrs() { + + // typedef for a pointer of nonlinear function of model equation + // returning vec (T, Z) + typedef arma::vec (*nvec_fnPtr)(const unsigned int t, + const arma::vec& alpha, const arma::vec& theta, + const arma::vec& known_params, const arma::mat& known_tv_params); + // typedef for a pointer of nonlinear fn returning mat (Tg, Zg, H, R) + typedef arma::mat (*nmat_fnPtr)(const unsigned int t, + const arma::vec& alpha, const arma::vec& theta, + const arma::vec& known_params, const arma::mat& known_tv_params); + + // typedef for a pointer returning a1 + typedef arma::vec (*a1_fnPtr)(const arma::vec& theta, + const arma::vec& known_params); + // typedef for a pointer returning P1 + typedef arma::mat (*P1_fnPtr)(const arma::vec& theta, + const arma::vec& known_params); + // typedef for a pointer of log-prior function + typedef double (*prior_fnPtr)(const arma::vec& theta); + + return Rcpp::List::create( + Rcpp::Named("a1_fn") = Rcpp::XPtr(new a1_fnPtr(&a1_fn)), + Rcpp::Named("P1_fn") = Rcpp::XPtr(new P1_fnPtr(&P1_fn)), + Rcpp::Named("Z_fn") = Rcpp::XPtr(new nvec_fnPtr(&Z_fn)), + Rcpp::Named("H_fn") = Rcpp::XPtr(new nmat_fnPtr(&H_fn)), + Rcpp::Named("T_fn") = Rcpp::XPtr(new nvec_fnPtr(&T_fn)), + Rcpp::Named("R_fn") = Rcpp::XPtr(new nmat_fnPtr(&R_fn)), + Rcpp::Named("Z_gn") = Rcpp::XPtr(new nmat_fnPtr(&Z_gn)), + Rcpp::Named("T_gn") = Rcpp::XPtr(new nmat_fnPtr(&T_gn)), + Rcpp::Named("log_prior_pdf") = + Rcpp::XPtr(new prior_fnPtr(&log_prior_pdf))); + } + ' + }) + if (!return_code) { + # create dummy variable to get rid of "undefined variable" note + create_xptrs <- NULL + Rcpp::sourceCpp(code = code) + create_xptrs() + } else code +} diff --git a/R/ekpf_filter.R b/R/ekpf_filter.R index 2b381799..654f1ebc 100644 --- a/R/ekpf_filter.R +++ b/R/ekpf_filter.R @@ -1,44 +1,84 @@ #' Extended Kalman Particle Filtering #' -#' Function \code{ekpf_filter} performs a extended Kalman particle filtering with stratification -#' resampling, based on Van Der Merwe et al (2001). +#' Function \code{ekpf_filter} performs a extended Kalman particle filtering +#' with stratification resampling, based on Van Der Merwe et al (2001). #' -#' @param object of class \code{ssm_nlg}. -#' @param particles Number of particles. -#' @param seed Seed for RNG. +#' @inheritParams bootstrap_filter +#' @param model Model of class \code{ssm_nlg}. #' @param ... Ignored. -#' @return A list containing samples, filtered estimates and the corresponding covariances, -#' weights, and an estimate of log-likelihood. -#' @references Van Der Merwe, R., Doucet, A., De Freitas, N., & Wan, E. A. (2001). The unscented particle filter. In Advances in neural information processing systems (pp. 584-590). +#' @return A list containing samples, filtered estimates and the +#' corresponding covariances, weights, and an estimate of log-likelihood. +#' @references Van Der Merwe, R., Doucet, A., De Freitas, N., & Wan, E. A. +#' (2001). The unscented particle filter. In Advances in neural +#' information processing systems (pp. 584-590). #' @export #' @rdname ekpf_filter -ekpf_filter <- function(object, particles, ...) { - UseMethod("ekpf_filter", object) +ekpf_filter <- function(model, particles, ...) { + UseMethod("ekpf_filter", model) } #' @method ekpf_filter ssm_nlg #' @export #' @rdname ekpf_filter -ekpf_filter.ssm_nlg <- function(object, particles, seed = sample(.Machine$integer.max, size = 1), ...) { +#' @examples +#' \donttest{ # Takes a while +#' set.seed(1) +#' n <- 50 +#' x <- y <- numeric(n) +#' y[1] <- rnorm(1, exp(x[1]), 0.1) +#' for(i in 1:(n-1)) { +#' x[i+1] <- rnorm(1, sin(x[i]), 0.1) +#' y[i+1] <- rnorm(1, exp(x[i+1]), 0.1) +#' } +#' +#' pntrs <- cpp_example_model("nlg_sin_exp") +#' +#' model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, +#' Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, +#' Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, +#' theta = c(log_H = log(0.1), log_R = log(0.1)), +#' log_prior_pdf = pntrs$log_prior_pdf, +#' n_states = 1, n_etas = 1, state_names = "state") +#' +#' out <- ekpf_filter(model_nlg, particles = 100) +#' ts.plot(cbind(x, out$at[1:n], out$att[1:n]), col = 1:3) +#'} +ekpf_filter.ssm_nlg <- function(model, particles, + seed = sample(.Machine$integer.max, size = 1), ...) { + + check_missingness(model) - if(missing(particles)) { + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument", + "`particles` instead.", sep = " ")) particles <- nsim } } + particles <- check_intmax(particles, "particles") + + nsamples <- ifelse(!is.null(nrow(model$y)), nrow(model$y), + length(model$y)) * model$n_states * particles + if (particles > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } + + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) - out <- ekpf(t(object$y), object$Z, object$H, object$T, - object$R, object$Z_gn, object$T_gn, object$a1, object$P1, - object$theta, object$log_prior_pdf, object$known_params, - object$known_tv_params, object$n_states, object$n_etas, - as.integer(object$time_varying), particles, + out <- ekpf(t(model$y), model$Z, model$H, model$T, + model$R, model$Z_gn, model$T_gn, model$a1, model$P1, + model$theta, model$log_prior_pdf, model$known_params, + model$known_tv_params, model$n_states, model$n_etas, + as.integer(model$time_varying), particles, seed) colnames(out$at) <- colnames(out$att) <- colnames(out$Pt) <- colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- - rownames(out$alpha) <- object$state_names - out$at <- ts(out$at, start = start(object$y), frequency = frequency(object$y)) - out$att <- ts(out$att, start = start(object$y), frequency = frequency(object$y)) + rownames(out$alpha) <- model$state_names + out$at <- ts(out$at, start = start(model$y), + frequency = frequency(model$y)) + out$att <- ts(out$att, start = start(model$y), + frequency = frequency(model$y)) out$alpha <- aperm(out$alpha, c(2, 1, 3)) out } diff --git a/R/expand_sample.R b/R/expand_sample.R new file mode 100644 index 00000000..a5bdd0ac --- /dev/null +++ b/R/expand_sample.R @@ -0,0 +1,93 @@ + +#' Expand the Jump Chain representation +#' +#' The MCMC algorithms of \code{bssm} use a jump chain representation where we +#' store the accepted values and the number of times we stayed in the current +#' value. Although this saves bit memory and is especially convenient for +#' IS-corrected MCMC, sometimes we want to have the usual sample paths +#' (for example for drawing traceplots). +#' Function \code{expand_sample} returns the expanded sample based on the +#' counts (in form of \code{coda::mcmc} object. Note that for +#' the IS-MCMC the expanded sample corresponds to the approximate posterior, +#' i.e., the weights are ignored. +#' +#' This functions is mostly for backwards compatibility, methods +#' \code{as.data.frame} and \code{as_draws} produce likely more convenient +#' output. +#' +#' @importFrom coda mcmc +#' @param x Output from \code{\link{run_mcmc}}. +#' @param variable Expand parameters \code{"theta"} or states \code{"states"}. +#' @param times A vector of indices. In case of states, +#' what time points to expand? Default is all. +#' @param states A vector of indices. In case of states, +#' what states to expand? Default is all. +#' @param by_states If \code{TRUE} (default), return list by states. +#' Otherwise by time. +#' @return An object of class \code{"mcmc"} of the \code{coda} package. +#' @seealso \code{as.data.frame.mcmc_output} and \code{as_draws.mcmc_output}. +#' @export +#' @examples +#' set.seed(1) +#' n <- 50 +#' x <- cumsum(rnorm(n)) +#' y <- rnorm(n, x) +#' model <- bsm_lg(y, sd_y = gamma_prior(1, 2, 2), +#' sd_level = gamma_prior(1, 2, 2)) +#' fit <- run_mcmc(model, iter = 1e4) +#' # Traceplots for theta +#' plot.ts(expand_sample(fit, variable = "theta")) +#' # Traceplot for x_5 +#' plot.ts(expand_sample(fit, variable = "states", times = 5, +#' states = 1)$level) +expand_sample <- function(x, variable = "theta", times, states, + by_states = TRUE) { + + if (!test_flag(by_states)) + stop("Argument 'by_states' should be TRUE or FALSE. ") + + variable <- match.arg(tolower(variable), c("theta", "states")) + if (x$mcmc_type %in% paste0("is", 1:3)) + warning(paste("Input is based on a IS-weighted MCMC, the results", + "correspond to the approximate posteriors.", sep = " ")) + + if (variable == "theta") { + out <- apply(x$theta, 2, rep, times = x$counts) + } else { + if (x$output_type == 1) { + if (missing(times)) { + times <- seq_len(nrow(x$alpha)) + } else { + if (!test_integerish(times, lower = 1, upper = nrow(x$alpha), + any.missing = FALSE, unique = TRUE)) + stop(paste0("Argument 'times' should contain indices between 1 and ", + nrow(x$alpha),".")) + } + if (missing(states)) { + states <- seq_len(ncol(x$alpha)) + } else { + if (!test_integerish(states, lower = 1, upper = ncol(x$alpha), + any.missing = FALSE, unique = TRUE)) + stop(paste0("Argument 'states' should contain indices between 1 and ", + ncol(x$alpha),".")) + } + + if (by_states) { + out <- lapply(states, function(i) { + z <- apply(x$alpha[times, i, , drop = FALSE], 1, rep, x$counts) + colnames(z) <- times + z + }) + names(out) <- colnames(x$alpha)[states] + } else { + out <- lapply(times, function(i) { + z <- apply(x$alpha[i, states, , drop = FALSE], 2, rep, x$counts) + colnames(z) <- colnames(x$alpha)[states] + z + }) + names(out) <- times + } + } else stop("MCMC output does not contain posterior samples of states.") + } + mcmc(out, start = x$burnin + 1, thin = x$thin) +} diff --git a/R/fitted.R b/R/fitted.R new file mode 100644 index 00000000..f86964d1 --- /dev/null +++ b/R/fitted.R @@ -0,0 +1,121 @@ +#' Fitted for State Space Model +#' +#' Returns summary statistics from the posterior predictive +#' distribution of the mean. +#' +#' @export +#' @importFrom stats fitted +#' @importFrom dplyr group_by ungroup summarise as_tibble +#' @importFrom diagis weighted_quantile weighted_var weighted_mean weighted_se +#' @name fitted.mcmc_output +#' @param object Results object of class \code{mcmc_output} from +#' \code{\link{run_mcmc}} based on the input model. +#' @param model A \code{bssm_model} object. +#' @param probs Numeric vector defining the quantiles of interest. Default is +#' \code{c(0.025, 0.975)}. +#' @param ... Ignored. +#' @examples +#' prior <- uniform(0.1 * sd(log10(UKgas)), 0, 1) +#' model <- bsm_lg(log10(UKgas), sd_y = prior, sd_level = prior, +#' sd_slope = prior, sd_seasonal = prior, period = 4) +#' fit <- run_mcmc(model, iter = 1e4) +#' res <- fitted(fit, model) +#' head(res) +#' +fitted.mcmc_output <- function(object, model, + probs = c(0.025, 0.975), ...) { + + if (!inherits(model, "bssm_model")) { + stop("Argument 'model' should be an object of class 'bssm_model'.") + } + if (inherits(model, c("ssm_mng", "ssm_mlg", "ssm_nlg"))) { + if (!identical(nrow(object$alpha) - 1L, nrow(model$y))) { + stop("Number of observations of the model and MCMC output do not match.") + } + } else { + if (!identical(nrow(object$alpha) - 1L, length(model$y))) { + stop("Number of observations of the model and MCMC output do not match.") + } + } + + if (any(probs < 0 | probs > 1)) stop("'probs' outside [0, 1].") + + n <- nrow(object$alpha) - 1L + m <- ncol(object$alpha) + + states <- aperm(object$alpha[1:n, , , drop = FALSE], c(2, 1, 3)) + theta <- t(object$theta) + + switch(attr(object, "model_type"), + ssm_mlg =, + ssm_ulg =, + bsm_lg =, + ar1_lg = { + if (!identical(length(model$a1), m)) { + stop(paste("Model does not correspond to the MCMC output:", + "Wrong number of states. ", sep = " ")) + } + pred <- gaussian_predict_past(model, theta, states, + 2L, 1L, + pmatch(attr(object, "model_type"), + c("ssm_mlg", "ssm_ulg", "bsm_lg", "ar1_lg")) - 1L) + + }, + ssm_mng =, + ssm_ung =, + bsm_ng =, + svm =, + ar1_ng = { + if (!identical(length(model$a1), m)) { + stop(paste("Model does not correspond to the MCMC output:", + "Wrong number of states. ", sep = " ")) + } + model$distribution <- pmatch(model$distribution, + c("svm", "poisson", "binomial", "negative binomial", "gamma", + "gaussian"), + duplicates.ok = TRUE) - 1 + pred <- nongaussian_predict_past(model, theta, states, + 2L, 1L, + pmatch(attr(object, "model_type"), + c("ssm_mng", "ssm_ung", "bsm_ng", "svm", "ar1_ng")) - 1L) + + if (anyNA(pred)) + warning("NA or NaN values in predictions, possible under/overflow?") + }, + ssm_nlg = { + if (!identical(model$n_states, m)) { + stop(paste("Model does not correspond to the MCMC output:", + "Wrong number of states. ", sep = " ")) + } + pred <- nonlinear_predict_past(t(model$y), model$Z, + model$H, model$T, model$R, model$Z_gn, + model$T_gn, model$a1, model$P1, + model$log_prior_pdf, model$known_params, + model$known_tv_params, as.integer(model$time_varying), + model$n_states, model$n_etas, + theta, states, 2L, 1L) + + } + , stop("Not yet implemented for ssm_sde. ")) + + variables <- colnames(model$y) + if (is.null(variables)) + variables <- paste("Series", 1:max(1, ncol(model$y))) + w <- object$counts * + (if (object$mcmc_type %in% paste0("is", 1:3)) object$weights else 1) + + + d <- data.frame(value = as.numeric(pred), + Variable = variables, + Time = rep(time(model$y), each = nrow(pred))) + + d |> dplyr::group_by(.data$Variable, .data$Time) |> + dplyr::summarise( + Mean = weighted_mean(.data$value, w), + SD = sqrt(weighted_var(.data$value, w)), + dplyr::as_tibble(as.list(weighted_quantile(.data$value, w, + probs = probs))), + "SE(Mean)" = as.numeric(sqrt(asymptotic_var(.data$value, w)))) |> + dplyr::ungroup() +} + diff --git a/R/importance_sample.R b/R/importance_sample.R index ab0fe4bf..9f190e3e 100644 --- a/R/importance_sample.R +++ b/R/importance_sample.R @@ -1,31 +1,36 @@ #' Importance Sampling from non-Gaussian State Space Model #' -#' Returns \code{nsim} samples from the approximating Gaussian model with corresponding -#' (scaled) importance weights. Probably mostly useful for comparing KFAS and bssm packages. +#' Returns \code{nsim} samples from the approximating Gaussian model with +#' corresponding (scaled) importance weights. +#' Probably mostly useful for comparing KFAS and bssm packages. #' -#' @param model of class \code{bsm_ng}, \code{ar1_ng} \code{svm}, \code{ssm_ung}, or \code{ssm_mng}. -#' @param nsim Number of samples. +#' @inheritParams gaussian_approx +#' @param model Model of class \code{bsm_ng}, \code{ar1_ng} \code{svm}, +#' \code{ssm_ung}, or \code{ssm_mng}. +#' @param nsim Number of samples (positive integer). Suitable values +#' depend on the model and the data, and while larger values provide more +#' accurate estimates, the run time also increases with respect to to the +#' number of samples, so it is generally a good idea to test the filter first +#' with a small number of samples, e.g., less than 100. #' @param use_antithetic Logical. If \code{TRUE} (default), use antithetic -#' variable for location in simulation smoothing. Ignored for \code{ssm_mng} models. -#' @param max_iter Maximum number of iterations used for the approximation. -#' @param conv_tol Convergence threshold for the approximation. Approximation is -#' claimed to be converged when the mean squared difference of the modes is -#' less than \code{conv_tol}. -#' @param seed Seed for the random number generator. +#' variable for location in simulation smoothing. Ignored for \code{ssm_mng} +#' models. +#' @param seed Seed for the C++ RNG (positive integer). #' @param ... Ignored. #' @export #' @rdname importance_sample #' @examples #' data("sexratio", package = "KFAS") -#' model <- bsm_ng(sexratio[, "Male"], sd_level = 0.001, u = sexratio[, "Total"], +#' model <- bsm_ng(sexratio[, "Male"], sd_level = 0.001, +#' u = sexratio[, "Total"], #' distribution = "binomial") #' #' imp <- importance_sample(model, nsim = 1000) #' #' est <- matrix(NA, 3, nrow(sexratio)) #' for(i in 1:ncol(est)) { -#' est[, i] <- Hmisc::wtd.quantile(exp(imp$alpha[i, 1, ]), imp$weights, -#' prob = c(0.05,0.5,0.95), normwt=TRUE) +#' est[, i] <- diagis::weighted_quantile(exp(imp$alpha[i, 1, ]), imp$weights, +#' prob = c(0.05,0.5,0.95)) #' } #' #' ts.plot(t(est),lty = c(2,1,2)) @@ -38,15 +43,30 @@ importance_sample <- function(model, nsim, use_antithetic, #' @rdname importance_sample #' @export importance_sample.nongaussian <- function(model, nsim, use_antithetic = TRUE, - max_iter = 100, conv_tol = 1e-8, seed = sample(.Machine$integer.max, size = 1), ...) { - - model$max_iter <- max_iter - model$conv_tol <- conv_tol + max_iter = 100, conv_tol = 1e-8, + seed = sample(.Machine$integer.max, size = 1), ...) { + + check_missingness(model) + + model$max_iter <- check_intmax(max_iter, "max_iter", positive = FALSE) + model$conv_tol <- check_positive_real(conv_tol, "conv_tol") + nsim <- check_intmax(nsim, "nsim") + + nsamples <- ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + length(model$a1) * nsim + if (nsim > 100 & nsamples > 1e12) { + warning(paste("Trying to sample ", nsamples, + "values, you might run out of memory.")) + } + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + if (!test_flag(use_antithetic)) + stop("Argument 'use_antithetic' should be TRUE or FALSE. ") model$distribution <- pmatch(model$distribution, c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), duplicates.ok = TRUE) - 1 - out <- importance_sample_ng(model, nsim, use_antithetic, seed, model_type(model)) + out <- importance_sample_ng(model, nsim, use_antithetic, seed, + model_type(model)) rownames(out$alpha) <- names(model$a1) out$alpha <- aperm(out$alpha, c(2, 1, 3)) out -} \ No newline at end of file +} diff --git a/R/init_mode.R b/R/init_mode.R index 0ee78eff..e85a4fc9 100644 --- a/R/init_mode.R +++ b/R/init_mode.R @@ -9,7 +9,7 @@ init_mode <- function(y, u, distribution) { y <- log(y) }, binomial = { - y <- qlogis((ifelse(is.na(y), 0.5, y) + 0.5)/(u + 1)) + y <- qlogis((ifelse(is.na(y), 0.5, y) + 0.5) / (u + 1)) }, gamma = { y <- y / u @@ -18,13 +18,14 @@ init_mode <- function(y, u, distribution) { }, "negative binomial" = { y <- y / u - y[is.na(y) | y < 1/6] <- 1/6 + y[is.na(y) | y < 1 / 6] <- 1 / 6 y <- log(y) }, gaussian = { }, - stop("Argument distribution must be 'poisson', 'binomial', 'gamma', 'gaussian', or 'negative binomial'.") + stop(paste("Argument distribution must be 'poisson', 'binomial', 'gamma',", + "'gaussian', or 'negative binomial'.", sep = " ")) ) y } diff --git a/R/kfilter.R b/R/kfilter.R index b40259e6..ed28b8ff 100644 --- a/R/kfilter.R +++ b/R/kfilter.R @@ -4,21 +4,24 @@ #' and returns the filtered estimates and one-step-ahead predictions of the #' states \eqn{\alpha_t} given the data up to time \eqn{t}. #' -#' For non-Gaussian models, the filtering is based on the approximate Gaussian model. +#' For non-Gaussian models, the filtering is based on the approximate +#' Gaussian model. #' -#' @param model Model Model object. +#' @param model Model of class \code{lineargaussian}, \code{nongaussian} or +#' \code{ssm_nlg}. #' @param ... Ignored. -#' @return List containing the log-likelihood (approximate in non-Gaussian case), -#' one-step-ahead predictions \code{at} and filtered -#' estimates \code{att} of states, and the corresponding variances \code{Pt} and -#' \code{Ptt}. +#' @return List containing the log-likelihood +#' (approximate in non-Gaussian case), one-step-ahead predictions \code{at} +#' and filtered estimates \code{att} of states, and the corresponding +#' variances \code{Pt} and \code{Ptt} up to the time point n+1 where n is the +#' length of the input time series. #' @seealso \code{\link{bootstrap_filter}} #' @export #' @rdname kfilter kfilter <- function(model, ...) { UseMethod("kfilter", model) } -#' @method kfilter gaussian +#' @method kfilter lineargaussian #' @rdname kfilter #' @export #' @examples @@ -26,11 +29,14 @@ kfilter <- function(model, ...) { #' y <- x + rnorm(20, sd = 0.1) #' model <- bsm_lg(y, sd_level = 1, sd_y = 0.1) #' ts.plot(cbind(y, x, kfilter(model)$att), col = 1:3) -kfilter.gaussian <- function(model, ...) { +kfilter.lineargaussian <- function(model, ...) { + + check_missingness(model) out <- gaussian_kfilter(model, model_type = model_type(model)) colnames(out$at) <- colnames(out$att) <- colnames(out$Pt) <- - colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- names(model$a1) + colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- + names(model$a1) out$at <- ts(out$at, start = start(model$y), frequency = frequency(model$y)) out$att <- ts(out$att, start = start(model$y), frequency = frequency(model$y)) out @@ -50,9 +56,10 @@ kfilter.nongaussian <- function(model, ...) { #' and returns the filtered estimates and one-step-ahead predictions of the #' states \eqn{\alpha_t} given the data up to time \eqn{t}. #' -#' @param model Model model -#' @param iekf_iter If \code{iekf_iter > 0}, iterated extended Kalman filter -#' is used with \code{iekf_iter} iterations. +#' @param model Model of class \code{ssm_nlg}. +#' @param iekf_iter Non-negative integer. The default zero corresponds to +#' normal EKF, whereas \code{iekf_iter > 0} corresponds to iterated EKF +#' with \code{iekf_iter} iterations. #' @return List containing the log-likelihood, #' one-step-ahead predictions \code{at} and filtered #' estimates \code{att} of states, and the corresponding variances \code{Pt} and @@ -60,8 +67,40 @@ kfilter.nongaussian <- function(model, ...) { #' @export #' @rdname ekf #' @export +#' @examples +#' \donttest{ # Takes a while on CRAN +#' set.seed(1) +#' mu <- -0.2 +#' rho <- 0.7 +#' sigma_y <- 0.1 +#' sigma_x <- 1 +#' x <- numeric(50) +#' x[1] <- rnorm(1, mu, sigma_x / sqrt(1 - rho^2)) +#' for(i in 2:length(x)) { +#' x[i] <- rnorm(1, mu * (1 - rho) + rho * x[i - 1], sigma_x) +#' } +#' y <- rnorm(50, exp(x), sigma_y) +#' +#' pntrs <- cpp_example_model("nlg_ar_exp") +#' +#' model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, +#' Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, +#' Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, +#' theta = c(mu= mu, rho = rho, +#' log_sigma_x = log(sigma_x), log_sigma_y = log(sigma_y)), +#' log_prior_pdf = pntrs$log_prior_pdf, +#' n_states = 1, n_etas = 1, state_names = "state") +#' +#' out_ekf <- ekf(model_nlg, iekf_iter = 0) +#' out_iekf <- ekf(model_nlg, iekf_iter = 5) +#' ts.plot(cbind(x, out_ekf$att, out_iekf$att), col = 1:3) +#' } ekf <- function(model, iekf_iter = 0) { + check_missingness(model) + + iekf_iter <- check_intmax(iekf_iter, "iekf_iter", positive = FALSE) + out <- ekf_nlg(t(model$y), model$Z, model$H, model$T, model$R, model$Z_gn, model$T_gn, model$a1, model$P1, model$theta, model$log_prior_pdf, model$known_params, @@ -69,7 +108,8 @@ ekf <- function(model, iekf_iter = 0) { as.integer(model$time_varying), iekf_iter) colnames(out$at) <- colnames(out$att) <- colnames(out$Pt) <- - colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- model$state_names + colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- + model$state_names out$at <- ts(out$at, start = start(model$y), frequency = frequency(model$y)) out$att <- ts(out$att, start = start(model$y), frequency = frequency(model$y)) @@ -82,8 +122,13 @@ ekf <- function(model, iekf_iter = 0) { #' and returns the filtered estimates and one-step-ahead predictions of the #' states \eqn{\alpha_t} given the data up to time \eqn{t}. #' -#' @param model Model model -#' @param alpha,beta,kappa Tuning parameters for the UKF. +#' @param model Model of class \code{ssm_nlg}. +#' @param alpha Positive tuning parameter of the UKF. Default is 0.001. Smaller +#' the value, closer the sigma point are to the mean of the state. +#' @param beta Non-negative tuning parameter of the UKF. The default value is +#' 2, which is optimal for Gaussian states. +#' @param kappa Non-negative tuning parameter of the UKF, which also affects +#' the spread of sigma points. Default value is 0. #' @return List containing the log-likelihood, #' one-step-ahead predictions \code{at} and filtered #' estimates \code{att} of states, and the corresponding variances \code{Pt} and @@ -91,7 +136,41 @@ ekf <- function(model, iekf_iter = 0) { #' @export #' @rdname ukf #' @export -ukf <- function(model, alpha = 1, beta = 0, kappa = 2) { +#' @examples +#' \donttest{ # Takes a while on CRAN +#' set.seed(1) +#' mu <- -0.2 +#' rho <- 0.7 +#' sigma_y <- 0.1 +#' sigma_x <- 1 +#' x <- numeric(50) +#' x[1] <- rnorm(1, mu, sigma_x / sqrt(1 - rho^2)) +#' for(i in 2:length(x)) { +#' x[i] <- rnorm(1, mu * (1 - rho) + rho * x[i - 1], sigma_x) +#' } +#' y <- rnorm(50, exp(x), sigma_y) +#' +#' pntrs <- cpp_example_model("nlg_ar_exp") +#' +#' model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, +#' Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, +#' Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, +#' theta = c(mu= mu, rho = rho, +#' log_sigma_x = log(sigma_x), log_sigma_y = log(sigma_y)), +#' log_prior_pdf = pntrs$log_prior_pdf, +#' n_states = 1, n_etas = 1, state_names = "state") +#' +#' out_iekf <- ekf(model_nlg, iekf_iter = 5) +#' out_ukf <- ukf(model_nlg, alpha = 0.01, beta = 2, kappa = 1) +#' ts.plot(cbind(x, out_iekf$att, out_ukf$att), col = 1:3) +#' } +ukf <- function(model, alpha = 0.001, beta = 2, kappa = 0) { + + check_missingness(model) + + if (alpha <= 0) stop("Parameter 'alpha' should be positive. ") + if (beta < 0) stop("Parameter 'beta' should be non-negative. ") + if (kappa < 0) stop("Parameter 'kappa' should be non-negative. ") out <- ukf_nlg(t(model$y), model$Z, model$H, model$T, model$R, model$Z_gn, model$T_gn, model$a1, model$P1, @@ -101,7 +180,8 @@ ukf <- function(model, alpha = 1, beta = 0, kappa = 2) { alpha, beta, kappa) colnames(out$at) <- colnames(out$att) <- colnames(out$Pt) <- - colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- model$state_names + colnames(out$Ptt) <- rownames(out$Pt) <- rownames(out$Ptt) <- + model$state_names out$at <- ts(out$at, start = start(model$y), frequency = frequency(model$y)) out$att <- ts(out$att, start = start(model$y), frequency = frequency(model$y)) diff --git a/R/loglik.R b/R/loglik.R index 353e327b..41c8e903 100644 --- a/R/loglik.R +++ b/R/loglik.R @@ -1,64 +1,97 @@ -#' Log-likelihood of a Gaussian State Space Model +#' Extract Log-likelihood of a State Space Model of class \code{bssm_model} #' -#' Computes the log-likelihood of a linear-Gaussian state space model of \code{bssm} package. +#' Computes the log-likelihood of a state space model defined by \code{bssm} +#' package. #' -#' @param object Model model. -#' @param ... Ignored. +#' @inheritParams particle_smoother +#' @param object Model of class \code{bssm_model}. +#' @param particles Number of samples for particle filter +#' (non-negative integer). If 0, approximate log-likelihood is returned either +#' based on the Gaussian approximation or EKF, depending on the \code{method} +#' argument. +#' @param method Sampling method. For Gaussian and non-Gaussian models with +#' linear dynamics,options are \code{"bsf"} (bootstrap particle filter, default +#' for non-linear models) and \code{"psi"} (\eqn{\psi}-APF, the default for +#' other models). For-nonlinear models option \code{"ekf"} +#' uses EKF/IEKF-based particle filter (or just EKF/IEKF approximation in the +#' case of \code{particles = 0}). #' @importFrom stats logLik -#' @method logLik gaussian -#' @rdname logLik +#' @method logLik lineargaussian +#' @rdname logLik_bssm +#' @return A numeric value. +#' @seealso particle_smoother #' @export -#' @examples +#' @references +#' Durbin, J., & Koopman, S. (2002). A Simple and Efficient Simulation +#' Smoother for State Space Time Series Analysis. Biometrika, 89(3), 603-615. +#' +#' Shephard, N., & Pitt, M. (1997). Likelihood Analysis of +#' Non-Gaussian Measurement Time Series. Biometrika, 84(3), 653-667. +#' +#' Gordon, NJ, Salmond, DJ, Smith, AFM (1993). +#' Novel approach to nonlinear/non-Gaussian Bayesian state estimation. +#' IEE Proceedings-F, 140, 107-113. +#' +#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators +#' based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 2020; 1-38. https://doi.org/10.1111/sjos.12492 +#' +#' Van Der Merwe, R, Doucet, A, De Freitas, N, Wan, EA (2001). +#' The unscented particle filter. +#' In Advances in neural information processing systems, p 584-590. +#' +#' Jazwinski, A 1970. Stochastic Processes and Filtering Theory. +#' Academic Press. +#' +#' Kitagawa, G (1996). Monte Carlo filter and smoother for non-Gaussian +#' nonlinear state space models. +#' Journal of Computational and Graphical Statistics, 5, 1-25. +#' @examples #' model <- ssm_ulg(y = c(1,4,3), Z = 1, H = 1, T = 1, R = 1) #' logLik(model) -logLik.gaussian <- function(object, ...) { +logLik.lineargaussian <- function(object, ...) { + + check_missingness(object) + gaussian_loglik(object, model_type(object)) } -#' Log-likelihood of a Non-Gaussian State Space Model -#' -#' Computes the log-likelihood of a non-Gaussian state space model of \code{bssm} package. -#' -#' @param object Model model. -#' @param particles Number of samples for particle filter or importance sampling. If 0, -#' approximate log-likelihood based on the Gaussian approximation is returned. -#' @param method Sampling method, default is psi-auxiliary filter (\code{"psi"}), -#' other choices are \code{"bsf"} bootstrap particle filter, and \code{"spdk"}, -#' which uses the importance sampling approach by Shephard and Pitt (1997) and -#' Durbin and Koopman (1997). -#' @param max_iter Maximum number of iterations for Gaussian approximation algorithm. -#' @param conv_tol Tolerance parameter for the approximation algorithm. -#' @param seed Seed for the random number generator. -#' @param ... Ignored. #' @method logLik nongaussian +#' @rdname logLik_bssm #' @export -#' @examples +#' @examples #' model <- ssm_ung(y = c(1,4,3), Z = 1, T = 1, R = 0.5, P1 = 2, #' distribution = "poisson") #' #' model2 <- bsm_ng(y = c(1,4,3), sd_level = 0.5, P1 = 2, #' distribution = "poisson") +#' #' logLik(model, particles = 0) #' logLik(model2, particles = 0) #' logLik(model, particles = 10, seed = 1) #' logLik(model2, particles = 10, seed = 1) logLik.nongaussian <- function(object, particles, method = "psi", - max_iter = 100, conv_tol = 1e-8, seed = sample(.Machine$integer.max, size = 1),...) { + max_iter = 100, conv_tol = 1e-8, + seed = sample(.Machine$integer.max, size = 1), ...) { + + check_missingness(object) - object$max_iter <- max_iter - object$conv_tol <- conv_tol + object$max_iter <- check_intmax(max_iter, "max_iter", positive = FALSE) + object$conv_tol <- check_positive_real(conv_tol, "conv_tol") - if(missing(particles)) { + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } - method <- match.arg(method, c("psi", "bsf", "spdk")) + method <- match.arg(tolower(method), c("psi", "bsf", "spdk")) method <- pmatch(method, c("psi", "bsf", "spdk")) - if (method == 2 && particles == 0) stop("'particles' must be positive for bootstrap filter.") + if (method == 2 && particles == 0) + stop("'particles' must be positive for bootstrap filter.") object$distribution <- pmatch(object$distribution, c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), @@ -66,43 +99,34 @@ logLik.nongaussian <- function(object, particles, method = "psi", nongaussian_loglik(object, particles, method, seed, model_type(object)) } -#' Log-likelihood of a Non-linear State Space Model -#' -#' Computes the log-likelihood of a state space model of class \code{ssm_nlg} package. -#' -#' @param object Model model. -#' @param particles Number of samples for particle filter. If 0, -#' approximate log-likelihood is returned either based on the Gaussian approximation or EKF, -#' depending on the \code{method} argument. -#' @param method Sampling method. Default is the bootstrap particle filter (\code{"bsf"}). -#' Other choices are \code{"psi"} which uses psi-auxiliary filter -#' (or approximating Gaussian model in the case of \code{particles = 0}), and \code{"ekf"} which -#' uses EKF-based particle filter (or just EKF approximation in the case of \code{particles = 0}). -#' @param max_iter Maximum number of iterations for gaussian approximation algorithm. -#' @param conv_tol Tolerance parameter for the approximation algorithm. -#' @param iekf_iter If \code{iekf_iter > 0}, iterated extended Kalman filter is used with -#' \code{iekf_iter} iterations in place of standard EKF. Defaults to zero. -#' @param seed Seed for the random number generator. -#' @param ... Ignored. #' @method logLik ssm_nlg +#' @rdname logLik_bssm #' @export logLik.ssm_nlg <- function(object, particles, method = "bsf", max_iter = 100, conv_tol = 1e-8, iekf_iter = 0, seed = sample(.Machine$integer.max, size = 1), ...) { - if(missing(particles)) { + check_missingness(object) + + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } method <- match.arg(method, c("psi", "bsf", "ekf")) if (method == "bsf" && particles == 0) - stop("'particles' must be positive for bootstrap particle filter.") + stop("'particles' must be positive for bootstrap filter.") method <- pmatch(method, c("psi", "bsf", NA, "ekf")) + max_iter <- check_intmax(max_iter, "max_iter", positive = FALSE) + conv_tol <- check_positive_real(conv_tol, "conv_tol") + iekf_iter <- check_intmax(iekf_iter, "iekf_iter", positive = FALSE) + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + nonlinear_loglik(t(object$y), object$Z, object$H, object$T, object$R, object$Z_gn, object$T_gn, object$a1, object$P1, object$theta, object$log_prior_pdf, object$known_params, @@ -110,31 +134,28 @@ logLik.ssm_nlg <- function(object, particles, method = "bsf", as.integer(object$time_varying), particles, seed, max_iter, conv_tol, iekf_iter, method) } -#' Log-likelihood of a State Space Model with SDE dynamics -#' -#' Computes the log-likelihood of a state space model of class \code{ssm_sde} package. -#' -#' @param object Model model. -#' @param particles Number of samples for particle filter. #' @param L Integer defining the discretization level defined as (2^L). -#' @param seed Seed for the random number generator. -#' @param ... Ignored. #' @method logLik ssm_sde +#' @rdname logLik_bssm #' @export logLik.ssm_sde <- function(object, particles, L, seed = sample(.Machine$integer.max, size = 1), ...) { - if(L <= 0) stop("Discretization level L must be larger than 0.") - if(missing(particles)) { + + check_missingness(object) + + if (L <= 0) stop("Discretization level L must be larger than 0.") + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + loglik_sde(object$y, object$x0, object$positive, object$drift, object$diffusion, object$ddiffusion, object$prior_pdf, object$obs_pdf, object$theta, particles, L, seed) } - - diff --git a/R/model_type.R b/R/model_type.R index 240b3ce5..4a82e039 100644 --- a/R/model_type.R +++ b/R/model_type.R @@ -1,5 +1,5 @@ model_type <- function(model) { - if(inherits(model, "gaussian")) { + if (inherits(model, "lineargaussian")) { switch(class(model)[1], "ssm_mlg" = 0L, "ssm_ulg" = 1L, diff --git a/R/models.R b/R/models.R index f0f49c8f..22956edd 100644 --- a/R/models.R +++ b/R/models.R @@ -1,50 +1,64 @@ +#' @srrstats {G2.3, G2.3a, G2.3b} match.arg and tolower used where applicable. +#' @srrstats {G2.7, G2.8, G2.9} Only matrix/mts/arrays as tabular data are +#' supported, not data.frame or similar objects. +#' @srrstats {G2.14, G2.14a, G2.14b, G2.14c, BS3.0} Missing observations are +#' handled automatically as per SSM theory, whereas missing values are not +#' allowed elsewhere. +#' @srrstats {BS1.0, BS1.1, BS1.2, BS1.2c} Examples and definitions of priors. +NULL + ## placeholder functions for fixed models -default_prior_fn <- function(theta) {0} -default_update_fn <- function(theta) {} +default_prior_fn <- function(theta) { + 0 +} +default_update_fn <- function(theta) { + +} #' #' General univariate linear-Gaussian state space models #' -#' Construct an object of class \code{ssm_ulg} by directly defining the corresponding terms of -#' the model. +#' Construct an object of class \code{ssm_ulg} by directly defining the +#' corresponding terms of the model. #' #' The general univariate linear-Gaussian model is defined using the following #' observational and state equations: #' -#' \deqn{y_t = D_t + Z_t \alpha_t + H_t \epsilon_t, (\textrm{observation equation})} -#' \deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, (\textrm{transition equation})} +#' \deqn{y_t = D_t + Z_t \alpha_t + H_t \epsilon_t, +#' (\textrm{observation equation})} +#' \deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, +#' (\textrm{transition equation})} #' #' where \eqn{\epsilon_t \sim N(0, 1)}, \eqn{\eta_t \sim N(0, I_k)} and #' \eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other. -#' Here k is the number of disturbance terms which can be less than m, the number of states. +#' Here k is the number of disturbance terms which can be less than m, the +#' number of states. #' #' The \code{update_fn} function should take only one #' vector argument which is used to create list with elements named as -#' \code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and \code{C}, +#' \code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, +#' and \code{C}, #' where each element matches the dimensions of the original model. -#' If any of these components is missing, it is assumed to be constant wrt. theta. +#' If any of these components is missing, it is assumed to be constant wrt. +#' theta. #' Note that while you can input say R as m x k matrix for \code{ssm_ulg}, #' \code{update_fn} should return R as m x k x 1 in this case. -#' It might be useful to first construct the model without updating function and then check -#' the expected structure of the model components from the output. +#' It might be useful to first construct the model without updating function +#' and then check the expected structure of the model components from the +#' output. #' -#' @param y Observations as time series (or vector) of length \eqn{n}. -#' @param Z System matrix Z of the observation equation as m x 1 or m x n matrix. -#' @param H Vector of standard deviations. Either a scalar or a vector of length n. -#' @param T System matrix T of the state equation. Either a m x m matrix or a -#' m x m x n array. -#' @param R Lower triangular matrix R the state equation. Either a m x k matrix or a -#' m x k x n array. -#' @param a1 Prior mean for the initial state as a vector of length m. -#' @param P1 Prior covariance matrix for the initial state as m x m matrix. -#' @param init_theta Initial values for the unknown hyperparameters theta. -#' @param D Intercept terms for observation equation, given as a length n vector. -#' @param C Intercept terms for state equation, given as m x n matrix. -#' @param update_fn Function which returns list of updated model -#' components given input vector theta. See details. -#' @param prior_fn Function which returns log of prior density -#' given input vector theta. -#' @param state_names Names for the states. -#' @return Object of class \code{ssm_ulg}. +#' @inheritParams ssm_ung +#' @param H A vector of standard deviations. Either a scalar or a vector of +#' length n. +#' @param update_fn A function which returns list of updated model +#' components given input vector theta. This function should take only one +#' vector argument which is used to create list with elements named as +#' \code{Z}, \code{H}, \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and +#' \code{C}, where each element matches the dimensions of the original model +#' It's best to check the internal dimensions with \code{str(model_object)} as +#' the dimensions of input arguments can differ from the final dimensions. +#' If any of these components is missing, it is assumed to be constant wrt. +#' theta. +#' @return An object of class \code{ssm_ulg}. #' @export #' @examples #' @@ -72,24 +86,32 @@ default_update_fn <- function(theta) {} #' } #' # prior for standard deviations as half-normal(1) #' prior_fn <- function(theta) { -#' if(any(theta < 0)){ -#' log_p <- -Inf +#' if(any(theta < 0)) { +#' log_p <- -Inf #' } else { -#' log_p <- sum(dnorm(theta, 0, 1, log = TRUE)) +#' log_p <- sum(dnorm(theta, 0, 1, log = TRUE)) #' } #' log_p #' } #' #' model <- ssm_ulg(y, Z, H, T, R, a1, P1, #' init_theta = c(1, 0.1, 0.1), -#' update_fn = update_fn, prior_fn = prior_fn) +#' update_fn = update_fn, prior_fn = prior_fn, +#' state_names = c("level", "b1", "b2"), +#' # using default values, but being explicit for testing purposes +#' C = matrix(0, 3, 1), D = numeric(1)) #' -#' out <- run_mcmc(model, iter = 10000) +#' out <- run_mcmc(model, iter = 5000) #' out -#' sumr <- summary(out, variable = "state") -#' ts.plot(sumr$Mean, col = 1:3) -#' lines(b1, col= 2, lty = 2) -#' lines(b2, col= 3, lty = 2) +#' sumr <- summary(out, variable = "state", times = 1:n) +#' sumr$true <- c(b1, b2, rep(1, n)) +#' library(ggplot2) +#' ggplot(sumr, aes(x = time, y = Mean)) + +#' geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`), alpha = 0.5) + +#' geom_line() + +#' geom_line(aes(y = true), colour = "red") + +#' facet_wrap(~ variable, scales = "free") + +#' theme_bw() #' #' # Perhaps easiest way to construct a general SSM for bssm is to use the #' # model building functionality of KFAS: @@ -108,33 +130,37 @@ default_update_fn <- function(theta) {} #' # (for large model it is more efficient to do this #' # "manually" by constructing only necessary matrices, #' # i.e., in this case a list with H and Q) -#' -#' updatefn <- function(theta){ +#' +#' prior_fn <- function(theta) { +#' if(any(theta < 0)) -Inf else sum(dnorm(theta, 0, 0.1, log = TRUE)) +#' } +#' +#' update_fn <- function(theta) { #' #' model_kfas <- SSModel(log(drivers) ~ SSMtrend(1, Q = theta[1]^2)+ #' SSMseasonal(period = 12, #' sea.type = "trigonometric", Q = theta[2]^2) + #' log(PetrolPrice) + law, data = Seatbelts, H = theta[3]^2) #' -#' as_bssm(model_kfas, kappa = 100) +#' # the bssm_model object is essentially list so this is fine +#' as_bssm(model_kfas, kappa = 100, init_theta = init_theta, +#' update_fn = update_fn, prior_fn = prior_fn) #' } #' -#' prior <- function(theta) { -#' if(any(theta < 0)) -Inf else sum(dnorm(theta, 0, 0.1, log = TRUE)) -#' } #' init_theta <- rep(1e-2, 3) -#' c("sd_level", "sd_seasonal", "sd_y") -#' model_bssm <- as_bssm(model_kfas, kappa = 100, -#' init_theta = init_theta, -#' prior_fn = prior, update_fn = updatefn) +#' names(init_theta) <- c("sd_level", "sd_seasonal", "sd_y") #' -#' \dontrun{ +#' model_bssm <- update_fn(init_theta) +#' +#' \donttest{ #' out <- run_mcmc(model_bssm, iter = 10000, burnin = 5000) #' out -#' -#' # Above the regression coefficients are modelled as time-invariant latent states. +#' } +#' # Above the regression coefficients are modelled as +#' # time-invariant latent states. #' # Here is an alternative way where we use variable D so that the -#' # coefficients are part of parameter vector theta: +#' # coefficients are part of parameter vector theta. Note however that the +#' # first option often preferable in order to keep the dimension of theta low. #' #' updatefn2 <- function(theta) { #' # note no PetrolPrice or law variables here @@ -160,80 +186,36 @@ default_update_fn <- function(theta) {} #' model_bssm2$theta <- init_theta #' model_bssm2$prior_fn <- prior2 #' model_bssm2$update_fn <- updatefn2 -#' +#' \donttest{ #' out2 <- run_mcmc(model_bssm2, iter = 10000, burnin = 5000) #' out2 #' } -ssm_ulg <- function(y, Z, H, T, R, a1, P1, init_theta = numeric(0), - D, C, state_names, update_fn = default_update_fn, prior_fn = default_prior_fn) { +ssm_ulg <- function(y, Z, H, T, R, a1 = NULL, P1 = NULL, + init_theta = numeric(0), + D = NULL, C = NULL, state_names, update_fn = default_update_fn, + prior_fn = default_prior_fn) { - check_y(y) + y <- check_y(y) n <- length(y) - if (length(Z) == 1) { - dim(Z) <- c(1, 1) - m <- 1 - } else { - if (!(dim(Z)[2] %in% c(1, NA, n))) - stop("Argument Z must be a (m x 1) or (m x n) matrix, - where m is the number of states and n is the length of the series. ") - m <- dim(Z)[1] - dim(Z) <- c(m, (n - 1) * (max(dim(Z)[2], 0, na.rm = TRUE) > 1) + 1) - } + # create Z + Z <- check_Z(Z, 1L, n) + m <- dim(Z)[1] + # create T - if (length(T) == 1 && m == 1) { - dim(T) <- c(1, 1, 1) - } else { - if ((length(T) == 1) || any(dim(T)[1:2] != m) || !(dim(T)[3] %in% c(1, NA, n))) - stop("Argument T must be a (m x m) matrix, (m x m x 1) or (m x m x n) array, where m is the number of states. ") - dim(T) <- c(m, m, (n - 1) * (max(dim(T)[3], 0, na.rm = TRUE) > 1) + 1) - } + T <- check_T(T, m, n) # create R - if (length(R) == m) { - dim(R) <- c(m, 1, 1) - k <- 1 - } else { - if (!(dim(R)[1] == m) || dim(R)[2] > m || !dim(R)[3] %in% c(1, NA, n)) - stop("Argument R must be a (m x k) matrix, (m x k x 1) or (m x k x n) array, where k<=m is the number of disturbances eta, and m is the number of states. ") - k <- dim(R)[2] - dim(R) <- c(m, k, (n - 1) * (max(dim(R)[3], 0, na.rm = TRUE) > 1) + 1) - } + R <- check_R(R, m, n) - # create a1 - if (missing(a1)) { - a1 <- rep(0, m) - } else { - if (length(a1) <= m) { - a1 <- rep(a1, length.out = m) - } else stop("Misspecified a1, argument a1 must be a vector of length m, where m is the number of state_names and 1<=t<=m.") - } - # create P1 - if (missing(P1)) { - P1 <- matrix(0, m, m) - } else { - if (length(P1) == 1 && m == 1) { - dim(P1) <- c(1, 1) - } else { - if (any(dim(P1)[1:2] != m)) - stop("Argument P1 must be (m x m) matrix, where m is the number of states. ") - } - } - if (!missing(D)) { - check_D(D, 1L, n) - D <- as.numeric(D) - } else { - D <- 0 - } - if (!missing(C)) { - check_C(C, m, n) - } else { - C <- matrix(0, m, 1) - } + a1 <- check_a1(a1, m) + + P1 <- check_P1(P1, m) - if (length(H)[3] %in% c(1, n)) - stop("Argument H must be a scalar or a vector of length n, where n is the length of the time series y.") + D <- check_D(D, 1L, n) + C <- check_C(C, m, n) + H <- check_H(H, 1L, n) if (missing(state_names)) { state_names <- paste("state", 1:m) @@ -241,70 +223,79 @@ ssm_ulg <- function(y, Z, H, T, R, a1, P1, init_theta = numeric(0), rownames(Z) <- colnames(T) <- rownames(T) <- rownames(R) <- names(a1) <- rownames(P1) <- colnames(P1) <- state_names - if(is.null(names(init_theta)) && length(init_theta) > 0) - names(init_theta) <- paste0("theta_", 1:length(init_theta)) + if (is.null(names(init_theta)) && length(init_theta) > 0) + names(init_theta) <- paste0("theta_", seq_along(init_theta)) # xreg and beta are need in C++ side in order to combine constructors structure(list(y = as.ts(y), Z = Z, H = H, T = T, R = R, a1 = a1, P1 = P1, D = D, C = C, update_fn = update_fn, prior_fn = prior_fn, theta = init_theta, - xreg = matrix(0,0,0), beta = numeric(0)), class = c("ssm_ulg", "gaussian")) + xreg = matrix(0, 0, 0), beta = numeric(0)), + class = c("ssm_ulg", "lineargaussian", "bssm_model")) } #' General univariate non-Gaussian state space model #' -#' Construct an object of class \code{ssm_ung} by directly defining the corresponding terms of -#' the model. +#' Construct an object of class \code{ssm_ung} by directly defining the +#' corresponding terms of the model. #' #' The general univariate non-Gaussian model is defined using the following #' observational and state equations: #' #' \deqn{p(y_t | D_t + Z_t \alpha_t), (\textrm{observation equation})} -#' \deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, (\textrm{transition equation})} +#' \deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, +#' (\textrm{transition equation})} #' #' where \eqn{\eta_t \sim N(0, I_k)} and #' \eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other, -#' and \eqn{p(y_t | .)} is either Poisson, binomial, gamma, or negative binomial distribution. -#' Here k is the number of disturbance terms which can be less than m, the number of states. +#' and \eqn{p(y_t | .)} is either Poisson, binomial, gamma, or +#' negative binomial distribution. +#' Here k is the number of disturbance terms which can be less than m, +#' the number of states. #' #' The \code{update_fn} function should take only one #' vector argument which is used to create list with elements named as -#' \code{Z}, \code{phi} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and \code{C}, +#' \code{Z}, \code{phi} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, +#' and \code{C}, #' where each element matches the dimensions of the original model. -#' If any of these components is missing, it is assumed to be constant wrt. theta. +#' If any of these components is missing, it is assumed to be constant +#' wrt. theta. #' Note that while you can input say R as m x k matrix for \code{ssm_ung}, #' \code{update_fn} should return R as m x k x 1 in this case. -#' It might be useful to first construct the model without updating function and then check -#' the expected structure of the model components from the output. +#' It might be useful to first construct the model without updating function +#' and then check the expected structure of the model components from +#' the output. #' +#' @inheritParams bsm_ng #' @param y Observations as time series (or vector) of length \eqn{n}. -#' @param Z System matrix Z of the observation equation. Either a vector of length m, +#' @param Z System matrix Z of the observation equation. Either a +#' vector of length m, #' a m x n matrix, or object which can be coerced to such. #' @param T System matrix T of the state equation. Either a m x m matrix or a #' m x m x n array, or object which can be coerced to such. -#' @param R Lower triangular matrix R the state equation. Either a m x k matrix or a -#' m x k x n array, or object which can be coerced to such. +#' @param R Lower triangular matrix R the state equation. Either +#' a m x k matrix or a m x k x n array, or object which can be coerced to such. #' @param a1 Prior mean for the initial state as a vector of length m. #' @param P1 Prior covariance matrix for the initial state as m x m matrix. -#' @param distribution Distribution of the observed time series. Possible choices are -#' \code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and \code{"negative binomial"}. -#' @param phi Additional parameter relating to the non-Gaussian distribution. -#' For negative binomial distribution this is the dispersion term, for gamma distribution -#' this is the shape parameter, and for other distributions this is ignored. -#' @param u Constant parameter vector for non-Gaussian models. For Poisson, gamma, and -#' negative binomial distribution, this corresponds to the offset term. For binomial, -#' this is the number of trials. -#' @param state_names Names for the states. +#' @param state_names A character vector defining the names of the states. #' @param C Intercept terms \eqn{C_t} for the state equation, given as a #' m times 1 or m times n matrix. #' @param D Intercept terms \eqn{D_t} for the observations equation, given as a #' scalar or vector of length n. -#' @param init_theta Initial values for the unknown hyperparameters theta. -#' @param update_fn Function which returns list of updated model -#' components given input vector theta. See details. -#' @param prior_fn Function which returns log of prior density +#' @param init_theta Initial values for the unknown hyperparameters theta +#' (i.e. unknown variables excluding latent state variables). +#' @param update_fn A function which returns list of updated model +#' components given input vector theta. This function should take only one +#' vector argument which is used to create list with elements named as +#' \code{Z}, \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, \code{C}, and +#' \code{phi}, where each element matches the dimensions of the original model. +#' If any of these components is missing, it is assumed to be constant wrt. +#' theta. It's best to check the internal dimensions with +#' \code{str(model_object)} as the dimensions of input arguments can differ +#' from the final dimensions. +#' @param prior_fn A function which returns log of prior density #' given input vector theta. -#' @return Object of class \code{ssm_ung}. +#' @return An object of class \code{ssm_ung}. #' @export #' @examples #' @@ -315,85 +306,35 @@ ssm_ulg <- function(y, Z, H, T, R, a1, P1, init_theta = numeric(0), #' # approximate results based on Gaussian approximation #' out <- smoother(model) #' ts.plot(cbind(model$y / model$u, exp(out$alphahat)), col = 1:2) -ssm_ung <- function(y, Z, T, R, a1, P1, distribution, phi = 1, u = 1, - init_theta = numeric(0), D, C, state_names, update_fn = default_update_fn, +ssm_ung <- function(y, Z, T, R, a1 = NULL, P1 = NULL, distribution, phi = 1, + u, init_theta = numeric(0), D = NULL, C = NULL, state_names, + update_fn = default_update_fn, prior_fn = default_prior_fn) { - distribution <- match.arg(distribution, + distribution <- match.arg(tolower(distribution), c("poisson", "binomial", "negative binomial", "gamma")) - check_y(y, distribution = distribution) + y <- check_y(y, distribution = distribution) n <- length(y) + if (missing(u)) u <- rep(1, n) + u <- check_u(u, y) + Z <- check_Z(Z, 1L, n) + m <- dim(Z)[1] - if (length(Z) == 1) { - dim(Z) <- c(1, 1) - m <- 1 - } else { - if (!(dim(Z)[2] %in% c(1, NA, n))) - stop("Argument Z must be a vector of length m, or (m x 1) or (m x n) matrix, - where m is the number of states and n is the length of the series. ") - m <- dim(Z)[1] - dim(Z) <- c(m, (n - 1) * (max(dim(Z)[2], 0, na.rm = TRUE) > 1) + 1) - } - # create T - if (length(T) == 1 && m == 1) { - dim(T) <- c(1, 1, 1) - } else { - if ((length(T) == 1) || any(dim(T)[1:2] != m) || !(dim(T)[3] %in% c(1, NA, n))) - stop("Argument T must be a (m x m) matrix, (m x m x 1) or (m x m x n) array, where m is the number of states. ") - dim(T) <- c(m, m, (n - 1) * (max(dim(T)[3], 0, na.rm = TRUE) > 1) + 1) - } + T <- check_T(T, m, n) # create R - if (length(R) == m) { - dim(R) <- c(m, 1, 1) - k <- 1 - } else { - if (!(dim(R)[1] == m) || dim(R)[2] > m || !dim(R)[3] %in% c(1, NA, n)) - stop("Argument R must be a (m x k) matrix, (m x k x 1) or (m x k x n) array, where k<=m is the number of disturbances eta, and m is the number of states. ") - k <- dim(R)[2] - dim(R) <- c(m, k, (n - 1) * (max(dim(R)[3], 0, na.rm = TRUE) > 1) + 1) - } + R <- check_R(R, m, n) - # create a1 - if (missing(a1)) { - a1 <- rep(0, m) - } else { - if (length(a1) <= m) { - a1 <- rep(a1, length.out = m) - } else stop("Misspecified a1, argument a1 must be a vector of length m, where m is the number of state_names and 1<=t<=m.") - } - # create P1 - if (missing(P1)) { - P1 <- matrix(0, m, m) - } else { - if (length(P1) == 1 && m == 1) { - dim(P1) <- c(1, 1) - } else { - if (any(dim(P1)[1:2] != m)) - stop("Argument P1 must be (m x m) matrix, where m is the number of states. ") - } - } + a1 <- check_a1(a1, m) - if (!missing(D)) { - check_D(D, 1L, n) - D <- as.numeric(D) - } else { - D <- 0 - } - if (!missing(C)) { - check_C(C, m, n) - } else { - C <- matrix(0, m, 1) - } + P1 <- check_P1(P1, m) - check_phi(phi) + D <- check_D(D, 1L, n) + C <- check_C(C, m, n) - if (length(u) == 1) { - u <- rep(u, length.out = n) - } - check_u(u) + check_phi(phi) initial_mode <- matrix(init_mode(y, u, distribution), ncol = 1) @@ -404,7 +345,7 @@ ssm_ung <- function(y, Z, T, R, a1, P1, distribution, phi = 1, u = 1, rownames(P1) <- colnames(P1) <- state_names if(is.null(names(init_theta)) && length(init_theta) > 0) - names(init_theta) <- paste0("theta_", 1:length(init_theta)) + names(init_theta) <- paste0("theta_", seq_along(init_theta)) # xreg and beta are need in C++ side in order to combine constructors structure(list(y = as.ts(y), Z = Z, T = T, R = R, a1 = a1, P1 = P1, @@ -412,20 +353,22 @@ ssm_ung <- function(y, Z, T, R, a1, P1, distribution, phi = 1, u = 1, initial_mode = initial_mode, update_fn = update_fn, prior_fn = prior_fn, theta = init_theta, max_iter = 100, conv_tol = 1e-8, local_approx = TRUE, - xreg = matrix(0,0,0), beta = numeric(0)), - class = c("ssm_ung", "nongaussian")) + xreg = matrix(0, 0, 0), beta = numeric(0)), + class = c("ssm_ung", "nongaussian", "bssm_model")) } #' General multivariate linear Gaussian state space models #' -#' Construct an object of class \code{ssm_mlg} by directly defining the corresponding terms of -#' the model. +#' Construct an object of class \code{ssm_mlg} by directly defining the +#' corresponding terms of the model. #' -#' The general multivariate linear-Gaussian model is defined using the following -#' observational and state equations: +#' The general multivariate linear-Gaussian model is defined using the +#' following observational and state equations: #' -#' \deqn{y_t = D_t + Z_t \alpha_t + H_t \epsilon_t, (\textrm{observation equation})} -#' \deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, (\textrm{transition equation})} +#' \deqn{y_t = D_t + Z_t \alpha_t + H_t \epsilon_t, +#' (\textrm{observation equation})} +#' \deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, +#' (\textrm{transition equation})} #' #' where \eqn{\epsilon_t \sim N(0, I_p)}, \eqn{\eta_t \sim N(0, I_k)} and #' \eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other. @@ -434,114 +377,66 @@ ssm_ung <- function(y, Z, T, R, a1, P1, distribution, phi = 1, u = 1, #' #' The \code{update_fn} function should take only one #' vector argument which is used to create list with elements named as -#' \code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and \code{C}, +#' \code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, +#' and \code{C}, #' where each element matches the dimensions of the original model. -#' If any of these components is missing, it is assumed to be constant wrt. theta. +#' If any of these components is missing, it is assumed to be +#' constant wrt. theta. #' Note that while you can input say R as m x k matrix for \code{ssm_mlg}, #' \code{update_fn} should return R as m x k x 1 in this case. #' It might be useful to first construct the model without updating function #' -#' @param y Observations as multivariate time series or matrix with dimensions n x p. -#' @param Z System matrix Z of the observation equation as p x m matrix or p x m x n array. -#' @param H Lower triangular matrix H of the observation. Either a scalar or a vector of length n. +#' @inheritParams ssm_ulg +#' @param y Observations as multivariate time series or matrix with +#' dimensions n x p. +#' @param Z System matrix Z of the observation equation as p x m matrix or +#' p x m x n array. +#' @param H Lower triangular matrix H of the observation. Either a scalar or +#' a vector of length n. #' @param T System matrix T of the state equation. Either a m x m matrix or a #' m x m x n array. -#' @param R Lower triangular matrix R the state equation. Either a m x k matrix or a -#' m x k x n array. -#' @param a1 Prior mean for the initial state as a vector of length m. -#' @param P1 Prior covariance matrix for the initial state as m x m matrix. -#' @param init_theta Initial values for the unknown hyperparameters theta. +#' @param R Lower triangular matrix R the state equation. Either a m x k matrix +#' or a m x k x n array. #' @param D Intercept terms for observation equation, given as a p x n matrix. #' @param C Intercept terms for state equation, given as m x n matrix. -#' @param update_fn Function which returns list of updated model -#' components given input vector theta. This function should take only one -#' vector argument which is used to create list with elements named as -#' \code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and \code{C}, -#' where each element matches the dimensions of the original model. -#' If any of these components is missing, it is assumed to be constant wrt. theta. -#' @param prior_fn Function which returns log of prior density -#' given input vector theta. -#' @param state_names Names for the states. -#' @return Object of class \code{ssm_mlg}. +#' @return An object of class \code{ssm_mlg}. #' @export #' @examples #' #' data("GlobalTemp", package = "KFAS") #' model_temp <- ssm_mlg(GlobalTemp, H = matrix(c(0.15,0.05,0, 0.05), 2, 2), -#' R = 0.05, Z = matrix(1, 2, 1), T = 1, P1 = 10) -#' ts.plot(cbind(model_temp$y, smoother(model_temp)$alphahat),col=1:3) -#' -ssm_mlg <- function(y, Z, H, T, R, a1, P1, init_theta = numeric(0), - D, C, state_names, update_fn = default_update_fn, prior_fn = default_prior_fn) { +#' R = 0.05, Z = matrix(1, 2, 1), T = 1, P1 = 10, +#' state_names = "temperature", +#' # using default values, but being explicit for testing purposes +#' D = matrix(0, 2, 1), C = matrix(0, 1, 1)) +#' ts.plot(cbind(model_temp$y, smoother(model_temp)$alphahat), col = 1:3) +#' +ssm_mlg <- function(y, Z, H, T, R, a1 = NULL, P1 = NULL, + init_theta = numeric(0), D = NULL, C = NULL, state_names, + update_fn = default_update_fn, prior_fn = default_prior_fn) { # create y - check_y(y, multivariate = TRUE) + y <- check_y(y, multivariate = TRUE) n <- nrow(y) p <- ncol(y) - + # create Z - if (dim(Z)[1] != p || !(dim(Z)[3] %in% c(1, NA, n))) - stop("Argument Z must be a (p x m) matrix or (p x m x n) array - where p is the number of series, m is the number of states, and n is the length of the series. ") + Z <- check_Z(Z, p, n, multivariate = TRUE) m <- dim(Z)[2] - dim(Z) <- c(p, m, (n - 1) * (max(dim(Z)[3], 0, na.rm = TRUE) > 1) + 1) - # create T - if (length(T) == 1 && m == 1) { - dim(T) <- c(1, 1, 1) - } else { - if ((length(T) == 1) || any(dim(T)[1:2] != m) || !(dim(T)[3] %in% c(1, NA, n))) - stop("Argument T must be a (m x m) matrix, (m x m x 1) or (m x m x n) array, where m is the number of states. ") - dim(T) <- c(m, m, (n - 1) * (max(dim(T)[3], 0, na.rm = TRUE) > 1) + 1) - } + T <- check_T(T, m, n) # create R - if (length(R) == m) { - dim(R) <- c(m, 1, 1) - k <- 1 - } else { - if (!(dim(R)[1] == m) || dim(R)[2] > m || !dim(R)[3] %in% c(1, NA, n)) - stop("Argument R must be a (m x k) matrix, (m x k x 1) or (m x k x n) array, where k<=m is the number of disturbances eta, and m is the number of states. ") - k <- dim(R)[2] - dim(R) <- c(m, k, (n - 1) * (max(dim(R)[3], 0, na.rm = TRUE) > 1) + 1) - } + R <- check_R(R, m, n) - # create a1 - if (missing(a1)) { - a1 <- rep(0, m) - } else { - if (length(a1) <= m) { - a1 <- rep(a1, length.out = m) - } else stop("Misspecified a1, argument a1 must be a vector of length m, where m is the number of state_names and 1<=t<=m.") - } - # create P1 - if (missing(P1)) { - P1 <- matrix(0, m, m) - } else { - if (length(P1) == 1 && m == 1) { - dim(P1) <- c(1, 1) - } else { - if (any(dim(P1)[1:2] != m)) - stop("Argument P1 must be (m x m) matrix, where m is the number of states. ") - } - } - - if (!missing(D)) { - check_D(D, p, n) - } else { - D <- matrix(0, p, 1) - } - if (!missing(C)) { - check_C(C, m, n) - } else { - C <- matrix(0, m, 1) - } + a1 <- check_a1(a1, m) + P1 <- check_P1(P1, m) - # create H - if (any(dim(H)[1:2] != p) || !(dim(H)[3] %in% c(1, n, NA))) - stop("Argument H must be a p x p matrix or a p x p x n array.") - dim(H) <- c(p, p, (n - 1) * (max(dim(H)[3], 0, na.rm = TRUE) > 1) + 1) + D <- check_D(D, p, n) + D <- as.matrix(D) # p = 1 + C <- check_C(C, m, n) + H <- check_H(H, p, n, multivariate = TRUE) if (missing(state_names)) { state_names <- paste("state", 1:m) @@ -550,146 +445,134 @@ ssm_mlg <- function(y, Z, H, T, R, a1, P1, init_theta = numeric(0), rownames(P1) <- colnames(P1) <- state_names if(is.null(names(init_theta)) && length(init_theta) > 0) - names(init_theta) <- paste0("theta_", 1:length(init_theta)) + names(init_theta) <- paste0("theta_", seq_along(init_theta)) structure(list(y = as.ts(y), Z = Z, H = H, T = T, R = R, a1 = a1, P1 = P1, D = D, C = C, update_fn = update_fn, prior_fn = prior_fn, theta = init_theta, - state_names = state_names), class = c("ssm_mlg", "gaussian")) + state_names = state_names), class = c("ssm_mlg", "lineargaussian", + "bssm_model")) } #' General Non-Gaussian State Space Model #' -#' Construct an object of class \code{ssm_mng} by directly defining the corresponding terms of -#' the model. +#' Construct an object of class \code{ssm_mng} by directly defining the +#' corresponding terms of the model. #' #' The general multivariate non-Gaussian model is defined using the following #' observational and state equations: #' #' \deqn{p^i(y^i_t | D_t + Z_t \alpha_t), (\textrm{observation equation})} -#' \deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, (\textrm{transition equation})} +#' \deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, +#' (\textrm{transition equation})} #' #' where \eqn{\eta_t \sim N(0, I_k)} and -#' \eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other, and \eqn{p^i(y_t | .)} -#' is either Poisson, binomial, gamma, Gaussian, or negative binomial distribution for -#' each observation series \eqn{i=1,...,p}.Here k is the number of disturbance terms -#' (which can be less than m, the number of states). -#' -#' @param y Observations as multivariate time series or matrix with dimensions n x p. -#' @param Z System matrix Z of the observation equation as p x m matrix or p x m x n array. +#' \eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other, and +#' \eqn{p^i(y_t | .)} is either Poisson, binomial, gamma, Gaussian, or +#' negative binomial distribution for each observation series \eqn{i=1,...,p}. +#' Here k is the number of disturbance terms (which can be less than m, +#' the number of states). +#' @inheritParams ssm_ung +#' @param y Observations as multivariate time series or matrix with dimensions +#' n x p. +#' @param Z System matrix Z of the observation equation as p x m matrix or +#' p x m x n array. #' @param T System matrix T of the state equation. Either a m x m matrix or a #' m x m x n array. -#' @param R Lower triangular matrix R the state equation. Either a m x k matrix or a +#' @param R Lower triangular matrix R the state equation. Either a m x k +#' matrix or a #' m x k x n array. -#' @param a1 Prior mean for the initial state as a vector of length m. -#' @param P1 Prior covariance matrix for the initial state as m x m matrix. -#' @param distribution vector of distributions of the observed series. Possible choices are -#' \code{"poisson"}, \code{"binomial"}, \code{"negative binomial"}, \code{"gamma"}, -#' and \code{"gaussian"}. +#' @param distribution A vector of distributions of the observed series. +#' Possible choices are +#' \code{"poisson"}, \code{"binomial"}, \code{"negative binomial"}, +#' \code{"gamma"}, and \code{"gaussian"}. #' @param phi Additional parameters relating to the non-Gaussian distributions. -#' For negative binomial distribution this is the dispersion term, for gamma distribution -#' this is the shape parameter, for Gaussian this is standard deviation, -#' and for other distributions this is ignored. -#' @param u Constant parameter for non-Gaussian models. For Poisson, gamma, -#' and negative binomial distribution, this corresponds to the offset term. -#' For binomial, this is the number of trials. -#' @param init_theta Initial values for the unknown hyperparameters theta. +#' For negative binomial distribution this is the dispersion term, for +#' gamma distribution this is the shape parameter, for Gaussian this is +#' standard deviation, and for other distributions this is ignored. +#' @param u A matrix of positive constants for non-Gaussian models +#' (of same dimensions as y). For Poisson, gamma, and negative binomial +#' distribution, this corresponds to the offset term. For binomial, this is the +#' number of trials (and as such should be integer(ish)). #' @param D Intercept terms for observation equation, given as p x n matrix. #' @param C Intercept terms for state equation, given as m x n matrix. -#' @param update_fn Function which returns list of updated model -#' components given input vector theta. This function should take only one -#' vector argument which is used to create list with elements named as -#' \code{Z}, \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, \code{C}, and -#' \code{phi}, -#' where each element matches the dimensions of the original model. -#' If any of these components is missing, it is assumed to be constant wrt. theta. -#' @param prior_fn Function which returns log of prior density -#' given input vector theta. -#' @param state_names Names for the states. -#' @return Object of class \code{ssm_mng}. +#' @return An object of class \code{ssm_mng}. #' @export -ssm_mng <- function(y, Z, T, R, a1, P1, distribution, phi = 1, u = 1, - init_theta = numeric(0), D, C, state_names, update_fn = default_update_fn, - prior_fn = default_prior_fn) { +#' @examples +#' +#' set.seed(1) +#' n <- 20 +#' x <- cumsum(rnorm(n, sd = 0.5)) +#' phi <- 2 +#' y <- cbind( +#' rgamma(n, shape = phi, scale = exp(x) / phi), +#' rbinom(n, 10, plogis(x))) +#' +#' Z <- matrix(1, 2, 1) +#' T <- 1 +#' R <- 0.5 +#' a1 <- 0 +#' P1 <- 1 +#' +#' update_fn <- function(theta) { +#' list(R = array(theta[1], c(1, 1, 1)), phi = c(theta[2], 1)) +#' } +#' +#' prior_fn <- function(theta) { +#' ifelse(all(theta > 0), sum(dnorm(theta, 0, 1, log = TRUE)), -Inf) +#' } +#' +#' model <- ssm_mng(y, Z, T, R, a1, P1, phi = c(2, 1), +#' init_theta = c(0.5, 2), +#' distribution = c("gamma", "binomial"), +#' u = cbind(1, rep(10, n)), +#' update_fn = update_fn, prior_fn = prior_fn, +#' state_names = "random_walk", +#' # using default values, but being explicit for testing purposes +#' D = matrix(0, 2, 1), C = matrix(0, 1, 1)) +#' +#' # smoothing based on approximating gaussian model +#' ts.plot(cbind(y, fast_smoother(model)), +#' col = 1:3, lty = c(1, 1, 2)) +#' +ssm_mng <- function(y, Z, T, R, a1 = NULL, P1 = NULL, distribution, + phi = 1, u, init_theta = numeric(0), D = NULL, C = NULL, state_names, + update_fn = default_update_fn, prior_fn = default_prior_fn) { # create y - check_y(y, multivariate = TRUE) + y <- check_y(y, multivariate = TRUE) n <- nrow(y) p <- ncol(y) + + if (missing(u)) u <- matrix(1, n, p) + u <- check_u(u, y, multivariate = TRUE) + if(length(distribution) == 1) distribution <- rep(distribution, p) check_distribution(y, distribution) if(length(phi) == 1) phi <- rep(phi, p) for(i in 1:p) { - distribution[i] <- match.arg(distribution[i], + distribution[i] <- match.arg(tolower(distribution[i]), c("poisson", "binomial", "negative binomial", "gamma", "gaussian")) check_phi(phi[i]) } - # create Z - if (dim(Z)[1] != p || !(dim(Z)[3] %in% c(1, NA, n))) - stop("Argument Z must be a (p x m) matrix or (p x m x n) array - where p is the number of series, m is the number of states, and n is the length of the series. ") + Z <- check_Z(Z, p, n, multivariate = TRUE) m <- dim(Z)[2] - dim(Z) <- c(p, m, (n - 1) * (max(dim(Z)[3], 0, na.rm = TRUE) > 1) + 1) - # create T - if (length(T) == 1 && m == 1) { - dim(T) <- c(1, 1, 1) - } else { - if ((length(T) == 1) || any(dim(T)[1:2] != m) || !(dim(T)[3] %in% c(1, NA, n))) - stop("Argument T must be a (m x m) matrix, (m x m x 1) or (m x m x n) array, where m is the number of states. ") - dim(T) <- c(m, m, (n - 1) * (max(dim(T)[3], 0, na.rm = TRUE) > 1) + 1) - } + T <- check_T(T, m, n) # create R - if (length(R) == m) { - dim(R) <- c(m, 1, 1) - k <- 1 - } else { - if (!(dim(R)[1] == m) || dim(R)[2] > m || !dim(R)[3] %in% c(1, NA, n)) - stop("Argument R must be a (m x k) matrix, (m x k x 1) or (m x k x n) array, where k<=m is the number of disturbances eta, and m is the number of states. ") - k <- dim(R)[2] - dim(R) <- c(m, k, (n - 1) * (max(dim(R)[3], 0, na.rm = TRUE) > 1) + 1) - } + R <- check_R(R, m, n) - # create a1 - if (missing(a1)) { - a1 <- rep(0, m) - } else { - if (length(a1) <= m) { - a1 <- rep(a1, length.out = m) - } else stop("Misspecified a1, argument a1 must be a vector of length m, where m is the number of state_names and 1<=t<=m.") - } - # create P1 - if (missing(P1)) { - P1 <- matrix(0, m, m) - } else { - if (length(P1) == 1 && m == 1) { - dim(P1) <- c(1, 1) - } else { - if (any(dim(P1)[1:2] != m)) - stop("Argument P1 must be (m x m) matrix, where m is the number of states. ") - } - } + a1 <- check_a1(a1, m) + P1 <- check_P1(P1, m) - if (!missing(D)) { - check_D(D, p, n) - } else { - D <- matrix(0, p, 1) - } - if (!missing(C)) { - check_C(C, m, n) - } else { - C <- matrix(0, m, 1) - } + D <- check_D(D, p, n) + if (p == 1) D <- as.matrix(D) + C <- check_C(C, m, n) - if (length(u) == 1) { - u <- matrix(u, n, p) - } - check_u(u) - if(!identical(dim(y), dim(u))) stop("Dimensions of 'y' and 'u' do not match. ") initial_mode <- y for(i in 1:p) { initial_mode[, i] <- init_mode(y[, i], u[, i], distribution[i]) @@ -702,91 +585,68 @@ ssm_mng <- function(y, Z, T, R, a1, P1, distribution, phi = 1, u = 1, rownames(P1) <- colnames(P1) <- state_names if(is.null(names(init_theta)) && length(init_theta) > 0) - names(init_theta) <- paste0("theta_", 1:length(init_theta)) + names(init_theta) <- paste0("theta_", seq_along(init_theta)) - structure(list(y = as.ts(y), Z = Z, T = T, R = R, a1 = a1, P1 = P1, phi = phi, u = u, - D = D, C = C, distribution = distribution, + structure(list(y = as.ts(y), Z = Z, T = T, R = R, a1 = a1, P1 = P1, + phi = phi, u = u, D = D, C = C, distribution = distribution, initial_mode = initial_mode, update_fn = update_fn, prior_fn = prior_fn, theta = init_theta, max_iter = 100, conv_tol = 1e-8, local_approx = TRUE), - class = c("ssm_mng", "nongaussian")) + class = c("ssm_mng", "nongaussian", "bssm_model")) } #' Basic Structural (Time Series) Model #' -#' Constructs a basic structural model with local level or local trend component -#' and seasonal component. -#' -#' @param y Vector or a \code{\link{ts}} object of observations. -#' @param sd_y A fixed value or prior for the standard error of -#' observation equation. See \link[=uniform]{priors} for details. -#' @param sd_level A fixed value or a prior for the standard error -#' of the noise in level equation. See \link[=uniform]{priors} for details. -#' @param sd_slope A fixed value or a prior for the standard error -#' of the noise in slope equation. See \link[=uniform]{priors} for details. -#' If missing, the slope term is omitted from the model. -#' @param sd_seasonal A fixed value or a prior for the standard error -#' of the noise in seasonal equation. See \link[=uniform]{priors} for details. -#' If missing, the seasonal component is omitted from the model. -#' @param xreg Matrix containing covariates. -#' @param beta Prior for the regression coefficients. -#' @param period Length of the seasonal component i.e. the number of -#' @param a1 Prior means for the initial states (level, slope, seasonals). -#' Defaults to vector of zeros. -#' @param P1 Prior covariance for the initial states (level, slope, seasonals). -#' Default is diagonal matrix with 1000 on the diagonal. -#' @param D,C Intercept terms for observation and -#' state equations, given as a length n vector and m times n matrix respectively. -#' @return Object of class \code{bsm_lg}. +#' Constructs a basic structural model with local level or local trend +#' component and seasonal component. +#' +#' @inheritParams bsm_ng +#' @param sd_y Standard deviation of the noise of observation equation. +#' Should be an object of class \code{bssm_prior} or scalar +#' value defining a known value such as 0. +#' @param D Intercept terms for observation equation, given as a length n +#' numeric vector or a scalar in case of time-invariant intercept. +#' @param C Intercept terms for state equation, given as a m times n matrix +#' or m times 1 matrix in case of time-invariant intercept. +#' @return An object of class \code{bsm_lg}. #' @export #' @examples #' +#' set.seed(1) +#' n <- 50 +#' x <- rnorm(n) +#' level <- numeric(n) +#' level[1] <- rnorm(1) +#' for (i in 2:n) level[i] <- rnorm(1, -0.2 + level[i-1], sd = 0.1) +#' y <- rnorm(n, 2.1 + x + level) +#' model <- bsm_lg(y, sd_y = halfnormal(1, 5), sd_level = 0.1, a1 = level[1], +#' P1 = matrix(0, 1, 1), xreg = x, beta = normal(1, 0, 1), +#' D = 2.1, C = matrix(-0.2, 1, 1)) +#' +#' ts.plot(cbind(fast_smoother(model), level), col = 1:2) +#' #' prior <- uniform(0.1 * sd(log10(UKgas)), 0, 1) -#' model <- bsm_lg(log10(UKgas), sd_y = prior, sd_level = prior, -#' sd_slope = prior, sd_seasonal = prior) +#' # period here is redundant as frequency(UKgas) = 4 +#' model_UKgas <- bsm_lg(log10(UKgas), sd_y = prior, sd_level = prior, +#' sd_slope = prior, sd_seasonal = prior, period = 4) #' -#' mcmc_out <- run_mcmc(model, iter = 5000) +#' # Note small number of iterations for CRAN checks +#' mcmc_out <- run_mcmc(model_UKgas, iter = 5000) +#' summary(mcmc_out, return_se = TRUE) +#' # Use the summary method from coda: #' summary(expand_sample(mcmc_out, "theta"))$stat #' mcmc_out$theta[which.max(mcmc_out$posterior), ] #' sqrt((fit <- StructTS(log10(UKgas), type = "BSM"))$coef)[c(4, 1:3)] #' + bsm_lg <- function(y, sd_y, sd_level, sd_slope, sd_seasonal, - beta, xreg = NULL, period = frequency(y), a1, P1, D, C) { + beta, xreg = NULL, period, a1 = NULL, P1 = NULL, D = NULL, + C = NULL) { - check_y(y) + y <- check_y(y) n <- length(y) - if (is.null(xreg)) { - xreg <- matrix(0, 0, 0) - coefs <- numeric(0) - beta <- NULL - } else { - - if (missing(beta) || is.null(beta)) { - stop("No prior defined for beta. ") - } - if(!is_prior(beta) && !is_prior_list(beta)) { - stop("Prior for beta must be of class 'bssm_prior' or 'bssm_prior_list.") - } - - if (is.null(dim(xreg)) && length(xreg) == n) { - xreg <- matrix(xreg, n, 1) - } - - check_xreg(xreg, n) - nx <- ncol(xreg) - if (nx == 1 && is_prior_list(beta)) beta <- beta[[1]] - if(nx > 1) { - coefs <- sapply(beta, "[[", "init") - } else { - coefs <- beta$init - } - check_beta(coefs, nx) - if (nx > 0 && is.null(colnames(xreg))) { - colnames(xreg) <- paste0("coef_",1:ncol(xreg)) - } - names(coefs) <- colnames(xreg) - } + regression_part <- create_regression(beta, xreg, n) notfixed <- c("y" = 1, "level" = 1, "slope" = 1, "seasonal" = 1) @@ -834,10 +694,10 @@ bsm_lg <- function(y, sd_y, sd_level, sd_slope, sd_seasonal, seasonal_names <- NULL seasonal <- FALSE sd_seasonal <- NULL + period <- 1L } else { - if (period < 2) { - stop("Period of seasonal component must be larger than 1. ") - } + if (missing(period)) period <- frequency(y) + period <- check_period(period, n) if (is_prior(sd_seasonal)) { check_sd(sd_seasonal$init, "seasonal") } else { @@ -852,24 +712,15 @@ bsm_lg <- function(y, sd_y, sd_level, sd_slope, sd_seasonal, m <- as.integer(1L + as.integer(slope) + as.integer(seasonal) * (period - 1)) - if (missing(a1)) { - a1 <- numeric(m) - } else { - if (length(a1) != m) { - stop("Argument a1 must be a vector of length ", m) - } - } + a1 <- check_a1(a1, m) + if (missing(P1)) { P1 <- diag(100, m) } else { - if (is.null(dim(P1)) && length(P1) == 1L) { - P1 <- matrix(P1) - } - if (!identical(dim(P1), c(m, m))) { - stop("Argument P1 must be m x m matrix, where m = ", m) - } + P1 <- check_P1(P1, m) } + if (slope) { state_names <- c("level", "slope", seasonal_names) } else { @@ -922,35 +773,34 @@ bsm_lg <- function(y, sd_y, sd_level, sd_slope, sd_seasonal, rownames(T) <- colnames(T) <- rownames(R) <- state_names - if(ncol(xreg) > 1) { - priors <- c(list(sd_y, sd_level, sd_slope, sd_seasonal), beta) + if(ncol(regression_part$xreg) > 1) { + priors <- c(list(sd_y, sd_level, sd_slope, sd_seasonal), + regression_part$beta) } else { - priors <- list(sd_y, sd_level, sd_slope, sd_seasonal, beta) + priors <- list(sd_y, sd_level, sd_slope, sd_seasonal, + regression_part$beta) } - names(priors) <- c("sd_y", "sd_level", "sd_slope", "sd_seasonal", names(coefs)) - priors <- priors[sapply(priors, is_prior)] + names(priors) <- c("sd_y", "sd_level", "sd_slope", "sd_seasonal", + names(regression_part$coefs)) + priors <- priors[vapply(priors, is_prior, TRUE)] - if (!missing(D)) { - check_D(D, 1L, n) - } else { - D <- matrix(0) - } - if (!missing(C)) { - check_C(C, m, n) - } else { - C <- matrix(0, m, 1) - } - theta <- if (length(priors) > 0) sapply(priors, "[[", "init") else numeric(0) + D <- check_D(D, 1L, n) + C <- check_C(C, m, n) + + theta <- if (length(priors) > 0) { + vapply(priors, "[[", "init", FUN.VALUE = 1) + } else numeric(0) priors <- combine_priors(priors) structure(list(y = as.ts(y), Z = Z, H = H, T = T, R = R, - a1 = a1, P1 = P1, xreg = xreg, beta = coefs, - D = D, - C = C, + a1 = a1, P1 = P1, xreg = regression_part$xreg, + beta = regression_part$coefs, D = D, C = C, slope = slope, seasonal = seasonal, period = period, fixed = as.integer(!notfixed), - prior_distributions = priors$prior_distribution, prior_parameters = priors$parameters, - theta = theta), class = c("bsm_lg", "ssm_ulg", "gaussian")) + prior_distributions = priors$prior_distribution, + prior_parameters = priors$parameters, + theta = theta), class = c("bsm_lg", "ssm_ulg", "lineargaussian", + "bssm_model")) } #' Non-Gaussian Basic Structural (Time Series) Model @@ -959,46 +809,82 @@ bsm_lg <- function(y, sd_y, sd_level, sd_slope, sd_seasonal, #' local trend component, a seasonal component, and regression component #' (or subset of these components). #' -#' @param y Vector or a \code{\link{ts}} object of observations. -#' @param sd_level A fixed value or a prior for the standard error -#' of the noise in level equation. See \link[=uniform]{priors} for details. -#' @param sd_slope A fixed value or a prior for the standard error -#' of the noise in slope equation. See \link[=uniform]{priors} for details. -#' If missing, the slope term is omitted from the model. -#' @param sd_seasonal A fixed value or a prior for the standard error -#' of the noise in seasonal equation. See \link[=uniform]{priors} for details. -#' If missing, the seasonal component is omitted from the model. -#' @param sd_noise Prior for the standard error of the additional noise term. -#' See \link[=uniform]{priors} for details. If missing, no additional noise term is used. -#' @param distribution Distribution of the observed time series. Possible choices are -#' \code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and \code{"negative binomial"}. +#' @param y A vector or a \code{ts} object of observations. +#' @param sd_level Standard deviation of the noise of level equation. +#' Should be an object of class \code{bssm_prior} or scalar +#' value defining a known value such as 0. +#' @param sd_slope Standard deviation of the noise of slope equation. +#' Should be an object of class \code{bssm_prior}, scalar +#' value defining a known value such as 0, or missing, in which case the slope +#' term is omitted from the model. +#' @param sd_seasonal Standard deviation of the noise of seasonal equation. +#' Should be an object of class \code{bssm_prior}, scalar +#' value defining a known value such as 0, or missing, in which case the +#' seasonal term is omitted from the model. +#' @param sd_noise A prior for the standard deviation of the additional noise +#' term to be added to linear predictor, defined as an object of class +#' \code{bssm_prior}. If missing, no additional noise term is used. +#' @param distribution Distribution of the observed time series. Possible +#' choices are \code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and +#' \code{"negative binomial"}. #' @param phi Additional parameter relating to the non-Gaussian distribution. -#' For negative binomial distribution this is the dispersion term, for gamma distribution -#' this is the shape parameter, and for other distributions this is ignored. -#' @param u Constant parameter vector for non-Gaussian models. For Poisson, gamma, and -#' negative binomial distribution, this corresponds to the offset term. For binomial, -#' this is the number of trials. -#' @param beta Prior for the regression coefficients. -#' @param xreg Matrix containing covariates. -#' @param period Length of the seasonal component i.e. the number of -#' observations per season. Default is \code{frequency(y)}. +#' For negative binomial distribution this is the dispersion term, for gamma +#' distribution this is the shape parameter, and for other distributions this +#' is ignored. Should an object of class \code{bssm_prior} or +#' a positive scalar. +#' @param u A vector of positive constants for non-Gaussian models. For +#' Poisson, gamma, and negative binomial distribution, this corresponds to the +#' offset term. For binomial, this is the number of trials. +#' @param beta A prior for the regression coefficients. +#' Should be an object of class \code{bssm_prior} or \code{bssm_prior_list} +#' (in case of multiple coefficients) or missing in case of no covariates. +#' @param xreg A matrix containing covariates with number of rows matching the +#' length of \code{y}. Can also be \code{ts}, \code{mts} or similar object +#' convertible to matrix. +#' @param period Length of the seasonal pattern. +#' Must be a positive value greater than 2 and less than the length of the +#' input time series. Default is \code{frequency(y)}, +#' which can also return non-integer value (in which case error is given). #' @param a1 Prior means for the initial states (level, slope, seasonals). #' Defaults to vector of zeros. -#' @param P1 Prior covariance for the initial states (level, slope, seasonals). -#' Default is diagonal matrix with 1e5 on the diagonal. -#' @param C Intercept terms for state equation, given as a -#' m times n matrix. -#' @return Object of class \code{bsm_ng}. +#' @param P1 Prior covariance matrix for the initial states (level, slope, +#' seasonals).Default is diagonal matrix with 100 on the diagonal. +#' @param C Intercept terms for state equation, given as a m x n or m x 1 +#' matrix. +#' @return An object of class \code{bsm_ng}. #' @export #' @examples +#' # Same data as in Vihola, Helske, Franks (2020) +#' data(poisson_series) +#' s <- sd(log(pmax(0.1, poisson_series))) +#' model <- bsm_ng(poisson_series, sd_level = uniform(0.115, 0, 2 * s), +#' sd_slope = uniform(0.004, 0, 2 * s), P1 = diag(0.1, 2), +#' distribution = "poisson") +#' +#' \donttest{ +#' out <- run_mcmc(model, iter = 1e5, particles = 10) +#' summary(out, variable = "theta", return_se = TRUE) +#' # should be about 0.093 and 0.016 +#' summary(out, variable = "states", return_se = TRUE, +#' states = 1, times = c(1, 100)) +#' # should be about -0.075, 2.618 +#' } +#' #' model <- bsm_ng(Seatbelts[, "VanKilled"], distribution = "poisson", #' sd_level = halfnormal(0.01, 1), #' sd_seasonal = halfnormal(0.01, 1), #' beta = normal(0, 0, 10), -#' xreg = Seatbelts[, "law"]) -#' \dontrun{ +#' xreg = Seatbelts[, "law"], +#' # default values, just for illustration +#' period = 12L, +#' a1 = rep(0, 1 + 11), # level + period - 1 seasonal states +#' P1 = diag(1, 12), +#' C = matrix(0, 12, 1), +#' u = rep(1, nrow(Seatbelts))) +#' +#' \donttest{ #' set.seed(123) -#' mcmc_out <- run_mcmc(model, iter = 5000, particles = 10) +#' mcmc_out <- run_mcmc(model, iter = 5000, particles = 10, mcmc_type = "da") #' mcmc_out$acceptance_rate #' theta <- expand_sample(mcmc_out, "theta") #' plot(theta) @@ -1009,57 +895,48 @@ bsm_lg <- function(y, sd_y, sd_level, sd_slope, sd_seasonal, #' geom_point() + stat_density2d(aes(fill = ..level.., alpha = ..level..), #' geom = "polygon") + scale_fill_continuous(low = "green", high = "blue") + #' guides(alpha = "none") -#' -#' # Traceplot using as.data.frame method for MCMC output: +#' +#' # Traceplot using as.data.frame method for MCMC output #' library("dplyr") -#' as.data.frame(mcmc_out) %>% -#' filter(variable == "sd_level") %>% +#' as.data.frame(mcmc_out) |> +#' filter(variable == "sd_level") |> #' ggplot(aes(y = value, x = iter)) + geom_line() +#' #' } +#' # Model with slope term and additional noise to linear predictor to capture +#' # excess variation +#' model2 <- bsm_ng(Seatbelts[, "VanKilled"], distribution = "poisson", +#' sd_level = halfnormal(0.01, 1), +#' sd_seasonal = halfnormal(0.01, 1), +#' beta = normal(0, 0, 10), +#' xreg = Seatbelts[, "law"], +#' sd_slope = halfnormal(0.01, 0.1), +#' sd_noise = halfnormal(0.01, 1)) +#' +#' # instead of extra noise term, model using negative binomial distribution: +#' model3 <- bsm_ng(Seatbelts[, "VanKilled"], +#' distribution = "negative binomial", +#' sd_level = halfnormal(0.01, 1), +#' sd_seasonal = halfnormal(0.01, 1), +#' beta = normal(0, 0, 10), +#' xreg = Seatbelts[, "law"], +#' sd_slope = halfnormal(0.01, 0.1), +#' phi = gamma_prior(1, 5, 5)) +#' bsm_ng <- function(y, sd_level, sd_slope, sd_seasonal, sd_noise, - distribution, phi, u = 1, beta, xreg = NULL, period = frequency(y), a1, P1, - C) { - + distribution, phi, u, beta, xreg = NULL, period, + a1 = NULL, P1 = NULL, C = NULL) { - distribution <- match.arg(distribution, + distribution <- match.arg(tolower(distribution), c("poisson", "binomial", "negative binomial", "gamma")) - check_y(y, multivariate = FALSE, distribution) + y <- check_y(y, multivariate = FALSE, distribution) n <- length(y) - if (is.null(xreg)) { - xreg <- matrix(0, 0, 0) - coefs <- numeric(0) - beta <- NULL - } else { - - if (missing(beta) || is.null(beta)) { - stop("No prior defined for beta. ") - } - if(!is_prior(beta) && !is_prior_list(beta)) { - stop("Prior for beta must be of class 'bssm_prior' or 'bssm_prior_list.") - } - - if (is.null(dim(xreg)) && length(xreg) == n) { - xreg <- matrix(xreg, n, 1) - } - - check_xreg(xreg, n) - nx <- ncol(xreg) - if (nx == 1 && is_prior_list(beta)) beta <- beta[[1]] - if(nx > 1) { - coefs <- sapply(beta, "[[", "init") - } else { - coefs <- beta$init - } - check_beta(coefs, nx) - - if (nx > 0 && is.null(colnames(xreg))) { - colnames(xreg) <- paste0("coef_",1:ncol(xreg)) - } - names(coefs) <- colnames(xreg) - - } + if (missing(u)) u <- rep(1, n) + u <- check_u(u, y) + + regression_part <- create_regression(beta, xreg, n) notfixed <- c("level" = 1, "slope" = 1, "seasonal" = 1) @@ -1092,10 +969,10 @@ bsm_ng <- function(y, sd_level, sd_slope, sd_seasonal, sd_noise, seasonal_names <- NULL seasonal <- FALSE sd_seasonal <- NULL + period <- 1L } else { - if (period < 2) { - stop("Period of seasonal component must be larger than 1. ") - } + if (missing(period)) period <- frequency(y) + period <- check_period(period, n) if (is_prior(sd_seasonal)) { check_sd(sd_seasonal$init, "seasonal") } else { @@ -1116,25 +993,15 @@ bsm_ng <- function(y, sd_level, sd_slope, sd_seasonal, sd_noise, npar_R <- 1L + as.integer(slope) + as.integer(seasonal) + as.integer(noise) - m <- as.integer(1L + as.integer(slope) + as.integer(seasonal) * (period - 1) + as.integer(noise)) + m <- as.integer(1L + as.integer(slope) + + as.integer(seasonal) * (period - 1) + as.integer(noise)) - if (missing(a1)) { - a1 <- numeric(m) - } else { - if (length(a1) != m) { - stop("Argument a1 must be a vector of length ", m) - } - } + a1 <- check_a1(a1, m) if (missing(P1)) { P1 <- diag(100, m) } else { - if (is.null(dim(P1)) && length(P1) == 1L) { - P1 <- matrix(P1) - } - if (!identical(dim(P1), c(m, m))) { - stop("Argument P1 must be m x m matrix, where m = ", m) - } + P1 <- check_P1(P1, m) } if (slope) { @@ -1193,21 +1060,15 @@ bsm_ng <- function(y, sd_level, sd_slope, sd_seasonal, sd_noise, use_phi <- distribution %in% c("negative binomial", "gamma") if (use_phi) { if (is_prior(phi)) { - check_phi(phi$init, distribution) + check_phi(phi$init) phi_est <- TRUE } else { - check_phi(phi, distribution) + check_phi(phi) } } else { phi <- 1 } - check_u(u) - if (length(u) != n) { - u <- rep(u, length.out = n) - } - - initial_mode <- matrix(init_mode(y, u, distribution), ncol = 1) dim(T) <- c(m, m, 1) @@ -1216,76 +1077,108 @@ bsm_ng <- function(y, sd_level, sd_slope, sd_seasonal, sd_noise, names(a1) <- rownames(P1) <- colnames(P1) <- rownames(Z) <- rownames(T) <- colnames(T) <- rownames(R) <- state_names - if(ncol(xreg) > 1) { - priors <- c(list(sd_level, sd_slope, sd_seasonal, sd_noise, phi), beta) + if(ncol(regression_part$xreg) > 1) { + priors <- c(list(sd_level, sd_slope, sd_seasonal, sd_noise, phi), + regression_part$beta) } else { - priors <- list(sd_level, sd_slope, sd_seasonal, sd_noise, phi, beta) + priors <- list(sd_level, sd_slope, sd_seasonal, sd_noise, phi, + regression_part$beta) } names(priors) <- c("sd_level", "sd_slope", "sd_seasonal", "sd_noise", "phi", - names(coefs)) - priors <- priors[sapply(priors, is_prior)] + names(regression_part$coefs)) + priors <- priors[vapply(priors, is_prior, TRUE)] if (phi_est) { phi <- phi$init } - D <- matrix(0) + D <- numeric(1) + C <- check_C(C, m, n) - if (!missing(C)) { - check_C(C, m, n) - } else { - C <- matrix(0, m, 1) - } - theta <- if (length(priors) > 0) sapply(priors, "[[", "init") else numeric(0) + theta <- if (length(priors) > 0) { + vapply(priors, "[[", "init", FUN.VALUE = 1) + } else numeric(0) priors <- combine_priors(priors) structure(list(y = as.ts(y), Z = Z, T = T, R = R, - a1 = a1, P1 = P1, phi = phi, u = u, xreg = xreg, beta = coefs, - D = D, - C = C, + a1 = a1, P1 = P1, phi = phi, u = u, xreg = regression_part$xreg, + beta = regression_part$coefs, D = D, C = C, slope = slope, seasonal = seasonal, noise = noise, period = period, fixed = as.integer(!notfixed), distribution = distribution, initial_mode = initial_mode, - prior_distributions = priors$prior_distribution, prior_parameters = priors$parameters, + prior_distributions = priors$prior_distribution, + prior_parameters = priors$parameters, theta = theta, phi_est = phi_est, max_iter = 100, conv_tol = 1e-8, local_approx = TRUE), - class = c("bsm_ng", "ssm_ung", "nongaussian")) + class = c("bsm_ng", "ssm_ung", "nongaussian", "bssm_model")) } #' Stochastic Volatility Model #' #' Constructs a simple stochastic volatility model with Gaussian errors and -#' first order autoregressive signal. -#' -#' @param y Vector or a \code{\link{ts}} object of observations. -#' @param mu Prior for mu parameter of transition equation. -#' @param rho prior for autoregressive coefficient. -#' @param sd_ar Prior for the standard deviation of noise of the AR-process. -#' @param sigma Prior for sigma parameter of observation equation, internally denoted as phi. Ignored -#' if \code{mu} is provided. Note that typically parametrization using mu is preferred due to -#' better numerical properties and availability of better Gaussian approximation. -#' Most notably the global approximation approach does not work with sigma parameterization as -#' sigma is not a parameter of the resulting approximate model. -#' @return Object of class \code{svm}. +#' first order autoregressive signal. See the main vignette for details. +#' +#' @param y A numeric vector or a \code{\link{ts}} object of observations. +#' @param mu A prior for mu parameter of transition equation. +#' Should be an object of class \code{bssm_prior}. +#' @param rho A prior for autoregressive coefficient. +#' Should be an object of class \code{bssm_prior}. +#' @param sd_ar A prior for the standard deviation of noise of the AR-process. +#' Should be an object of class \code{bssm_prior}. +#' @param sigma A prior for sigma parameter of observation equation, internally +#' denoted as phi. Should be an object of class \code{bssm_prior}. +#' Ignored if \code{mu} is provided. Note that typically +#' parametrization using mu is preferred due to better numerical properties and +#' availability of better Gaussian approximation. +#' Most notably the global approximation approach does not work with sigma +#' parameterization as sigma is not a parameter of the resulting approximate +#' model. +#' @return An object of class \code{svm}. #' @export #' @rdname svm #' @examples #' #' data("exchange") -#' exchange <- exchange[1:100] # faster CRAN check -#' model <- svm(exchange, rho = uniform(0.98,-0.999,0.999), +#' y <- exchange[1:100] # for faster CRAN check +#' model <- svm(y, rho = uniform(0.98, -0.999, 0.999), #' sd_ar = halfnormal(0.15, 5), sigma = halfnormal(0.6, 2)) #' #' obj <- function(pars) { -#' -logLik(svm(exchange, rho = uniform(pars[1],-0.999,0.999), -#' sd_ar = halfnormal(pars[2],sd=5), -#' sigma = halfnormal(pars[3],sd=2)), particles = 0) +#' -logLik(svm(y, +#' rho = uniform(pars[1], -0.999, 0.999), +#' sd_ar = halfnormal(pars[2], 5), +#' sigma = halfnormal(pars[3], 2)), particles = 0) #' } -#' opt <- nlminb(c(0.98, 0.15, 0.6), obj, lower = c(-0.999, 1e-4, 1e-4), upper = c(0.999,10,10)) +#' opt <- optim(c(0.98, 0.15, 0.6), obj, +#' lower = c(-0.999, 1e-4, 1e-4), +#' upper = c(0.999, 10, 10), method = "L-BFGS-B") #' pars <- opt$par -#' model <- svm(exchange, rho = uniform(pars[1],-0.999,0.999), -#' sd_ar = halfnormal(pars[2],sd=5), -#' sigma = halfnormal(pars[3],sd=2)) +#' model <- svm(y, +#' rho = uniform(pars[1],-0.999,0.999), +#' sd_ar = halfnormal(pars[2], 5), +#' sigma = halfnormal(pars[3], 2)) +#' +#' # alternative parameterization +#' model2 <- svm(y, rho = uniform(0.98,-0.999, 0.999), +#' sd_ar = halfnormal(0.15, 5), mu = normal(0, 0, 1)) +#' +#' obj2 <- function(pars) { +#' -logLik(svm(y, +#' rho = uniform(pars[1], -0.999, 0.999), +#' sd_ar = halfnormal(pars[2], 5), +#' mu = normal(pars[3], 0, 1)), particles = 0) +#' } +#' opt2 <- optim(c(0.98, 0.15, 0), obj2, lower = c(-0.999, 1e-4, -Inf), +#' upper = c(0.999, 10, Inf), method = "L-BFGS-B") +#' pars2 <- opt2$par +#' model2 <- svm(y, +#' rho = uniform(pars2[1],-0.999,0.999), +#' sd_ar = halfnormal(pars2[2], 5), +#' mu = normal(pars2[3], 0, 1)) +#' +#' # sigma is internally stored in phi +#' ts.plot(cbind(model$phi * exp(0.5 * fast_smoother(model)), +#' exp(0.5 * fast_smoother(model2))), col = 1:2) #' svm <- function(y, mu, rho, sd_ar, sigma) { @@ -1293,7 +1186,7 @@ svm <- function(y, mu, rho, sd_ar, sigma) { stop("Define either sigma or mu, but not both.") } - check_y(y) + y <- check_y(y) check_rho(rho$init) check_sd(sd_ar$init, "rho") @@ -1304,7 +1197,8 @@ svm <- function(y, mu, rho, sd_ar, sigma) { } else { svm_type <- 0L check_sd(sigma$init, "sigma", FALSE) - initial_mode <- matrix(log(pmax(1e-4, y^2)) - 2 * log(sigma$init), ncol = 1) + initial_mode <- + matrix(log(pmax(1e-4, y^2)) - 2 * log(sigma$init), ncol = 1) } a1 <- if(svm_type) mu$init else 0 P1 <- matrix(sd_ar$init^2 / (1 - rho$init^2)) @@ -1317,88 +1211,90 @@ svm <- function(y, mu, rho, sd_ar, sigma) { rownames(T) <- colnames(T) <- rownames(R) <- "signal" priors <- list(rho, sd_ar, if(svm_type==0) sigma else mu) - priors <- priors[!sapply(priors, is.null)] + priors <- priors[!vapply(priors, is.null, TRUE)] names(priors) <- c("rho", "sd_ar", if(svm_type==0) "sigma" else "mu") C <- if (svm_type) matrix(mu$init * (1 - T[1])) else matrix(0) D <- matrix(0) - theta <- if (length(priors) > 0) sapply(priors, "[[", "init") else numeric(0) + theta <- if (length(priors) > 0) { + vapply(priors, "[[", "init", FUN.VALUE = 1) + } else numeric(0) priors <- combine_priors(priors) structure(list(y = as.ts(y), Z = Z, T = T, R = R, - a1 = a1, P1 = P1, phi = if (svm_type == 0) sigma$init else 1, xreg = matrix(0, 0, 0), + a1 = a1, P1 = P1, phi = if (svm_type == 0) sigma$init else 1, + xreg = matrix(0, 0, 0), beta = numeric(0), D = D, C = C, initial_mode = initial_mode, - svm_type = svm_type, distribution = "svm", u = 1, phi_est = !as.logical(svm_type), - prior_distributions = priors$prior_distribution, prior_parameters = priors$parameters, + svm_type = svm_type, distribution = "svm", u = 1, + phi_est = !as.logical(svm_type), + prior_distributions = priors$prior_distribution, + prior_parameters = priors$parameters, theta = theta, max_iter = 100, conv_tol = 1e-8, local_approx = TRUE), - class = c("svm", "ssm_ung", "nongaussian")) + class = c("svm", "ssm_ung", "nongaussian", "bssm_model")) } #' Non-Gaussian model with AR(1) latent process #' -#' Constructs a simple non-Gaussian model where the state dynamics follow an AR(1) process. -#' -#' @param y Vector or a \code{\link{ts}} object of observations. -#' @param rho prior for autoregressive coefficient. -#' @param mu A fixed value or a prior for the stationary mean of the latent AR(1) process. Parameter is omitted if this is set to 0. -#' @param sigma Prior for the standard deviation of noise of the AR-process. -#' @param beta Prior for the regression coefficients. -#' @param xreg Matrix containing covariates. -#' @param distribution Distribution of the observed time series. Possible choices are -#' \code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and \code{"negative binomial"}. -#' @param phi Additional parameter relating to the non-Gaussian distribution. -#' For negative binomial distribution this is the dispersion term, for gamma distribution -#' this is the shape parameter, and for other distributions this is ignored. -#' @param u Constant parameter vector for non-Gaussian models. For Poisson, gamma, and -#' negative binomial distribution, this corresponds to the offset term. For binomial, -#' this is the number of trials. -#' @return Object of class \code{ar1_ng}. +#' Constructs a simple non-Gaussian model where the state dynamics follow an +#' AR(1) process. +#' +#' @inheritParams bsm_ng +#' @param rho A prior for autoregressive coefficient. +#' Should be an object of class \code{bssm_prior}. +#' @param mu A fixed value or a prior for the stationary mean of the latent +#' AR(1) process. Should be an object of class \code{bssm_prior} or scalar +#' value defining a fixed mean such as 0. +#' @param sigma A prior for the standard deviation of noise of the AR-process. +#' Should be an object of class \code{bssm_prior} +#' @return An object of class \code{ar1_ng}. #' @export #' @rdname ar1_ng -ar1_ng <- function(y, rho, sigma, mu, distribution, phi, u = 1, beta, xreg = NULL) { +#' @examples +#' model <- ar1_ng(discoveries, rho = uniform(0.5,-1,1), +#' sigma = halfnormal(0.1, 1), mu = normal(0, 0, 1), +#' distribution = "poisson") +#' out <- run_mcmc(model, iter = 1e4, mcmc_type = "approx", +#' output_type = "summary") +#' +#' ts.plot(cbind(discoveries, exp(out$alphahat)), col = 1:2) +#' +#' set.seed(1) +#' n <- 30 +#' phi <- 2 +#' rho <- 0.9 +#' sigma <- 0.1 +#' beta <- 0.5 +#' u <- rexp(n, 0.1) +#' x <- rnorm(n) +#' z <- y <- numeric(n) +#' z[1] <- rnorm(1, 0, sigma / sqrt(1 - rho^2)) +#' y[1] <- rnbinom(1, mu = u * exp(beta * x[1] + z[1]), size = phi) +#' for(i in 2:n) { +#' z[i] <- rnorm(1, rho * z[i - 1], sigma) +#' y[i] <- rnbinom(1, mu = u * exp(beta * x[i] + z[i]), size = phi) +#' } +#' +#' model <- ar1_ng(y, rho = uniform_prior(0.9, 0, 1), +#' sigma = gamma_prior(0.1, 2, 10), mu = 0., +#' phi = gamma_prior(2, 2, 1), distribution = "negative binomial", +#' xreg = x, beta = normal_prior(0.5, 0, 1), u = u) +#' +ar1_ng <- function(y, rho, sigma, mu, distribution, phi, u, beta, + xreg = NULL) { - distribution <- match.arg(distribution, + distribution <- match.arg(tolower(distribution), c("poisson", "binomial", "negative binomial", "gamma")) - check_y(y, multivariate = FALSE, distribution) - + y <- check_y(y, multivariate = FALSE, distribution) n <- length(y) - if (is.null(xreg)) { - xreg <- matrix(0, 0, 0) - coefs <- numeric(0) - beta <- NULL - } else { - - if (missing(beta) || is.null(beta)) { - stop("No prior defined for beta. ") - } - if(!is_prior(beta) && !is_prior_list(beta)) { - stop("Prior for beta must be of class 'bssm_prior' or 'bssm_prior_list.") - } - n <- length(y) - if (is.null(dim(xreg)) && length(xreg) == n) { - xreg <- matrix(xreg, n, 1) - } - check_xreg(xreg, n) - nx <- ncol(xreg) - if (nx == 1 && is_prior_list(beta)) beta <- beta[[1]] - if(nx > 1) { - coefs <- sapply(beta, "[[", "init") - } else { - coefs <- beta$init - } - check_beta(coefs, nx) - - if (nx > 0 && is.null(colnames(xreg))) { - colnames(xreg) <- paste0("coef_",1:ncol(xreg)) - } - names(coefs) <- colnames(xreg) - - } + if (missing(u)) u <- rep(1, n) + u <- check_u(u, y) + + regression_part <- create_regression(beta, xreg, n) check_rho(rho$init) check_sd(sigma$init, "rho") @@ -1414,27 +1310,22 @@ ar1_ng <- function(y, rho, sigma, mu, distribution, phi, u = 1, beta, xreg = NUL a1 <- mu C <- matrix(mu * (1 - rho$init)) } - distribution <- match.arg(distribution, c("poisson", "binomial", + distribution <- match.arg(tolower(distribution), c("poisson", "binomial", "negative binomial", "gamma")) use_phi <- distribution %in% c("negative binomial", "gamma") phi_est <- FALSE if (use_phi) { if (is_prior(phi)) { - check_phi(phi$init, distribution) + check_phi(phi$init) phi_est <- TRUE } else { - check_phi(phi, distribution) + check_phi(phi) } } else { phi <- 1 } - check_u(u) - if (length(u) != n) { - u <- rep(u, length.out = n) - } - initial_mode <- matrix(init_mode(y, u, distribution), ncol = 1) P1 <- matrix(sigma$init^2 / (1 - rho$init^2)) @@ -1446,90 +1337,73 @@ ar1_ng <- function(y, rho, sigma, mu, distribution, phi, u = 1, beta, xreg = NUL rownames(T) <- colnames(T) <- rownames(R) <- "signal" - if(ncol(xreg) > 1) { - priors <- c(list(rho, sigma, mu, phi), beta) + if(ncol(regression_part$xreg) > 1) { + priors <- c(list(rho, sigma, mu, phi), regression_part$beta) } else { - priors <- list(rho, sigma, mu, phi, beta) + priors <- list(rho, sigma, mu, phi, regression_part$beta) } names(priors) <- - c("rho", "sigma", "mu", "phi", names(coefs)) - priors <- priors[sapply(priors, is_prior)] + c("rho", "sigma", "mu", "phi", names(regression_part$coefs)) + priors <- priors[vapply(priors, is_prior, TRUE)] if (phi_est) { phi <- phi$init } D <- matrix(0) - theta <- if (length(priors) > 0) sapply(priors, "[[", "init") else numeric(0) + theta <- if (length(priors) > 0) { + vapply(priors, "[[", "init", FUN.VALUE = 1) + } else numeric(0) priors <- combine_priors(priors) structure(list(y = as.ts(y), Z = Z, T = T, R = R, - a1 = a1, P1 = P1, phi = phi, u = u, xreg = xreg, beta = coefs, + a1 = a1, P1 = P1, phi = phi, u = u, + xreg = regression_part$xreg, beta = regression_part$coefs, D = D, C = C, initial_mode = initial_mode, distribution = distribution, mu_est = mu_est, phi_est = phi_est, - prior_distributions = priors$prior_distribution, prior_parameters = priors$parameters, - theta = theta, + prior_distributions = priors$prior_distribution, + prior_parameters = priors$parameters, theta = theta, max_iter = 100, conv_tol = 1e-8, local_approx = TRUE), - class = c("ar1_ng", "ssm_ung", "nongaussian")) + class = c("ar1_ng", "ssm_ung", "nongaussian", "bssm_model")) } #' Univariate Gaussian model with AR(1) latent process #' -#' Constructs a simple Gaussian model where the state dynamics follow an AR(1) process. -#' -#' @param y Vector or a \code{\link{ts}} object of observations. -#' @param rho prior for autoregressive coefficient. -#' @param mu A fixed value or a prior for the stationary mean of the latent AR(1) process. Parameter is omitted if this is set to 0. -#' @param sigma Prior for the standard deviation of noise of the AR-process. -#' @param sd_y Prior for the standard deviation of observation equation. -#' @param beta Prior for the regression coefficients. -#' @param xreg Matrix containing covariates. -#' @return Object of class \code{ar1_lg}. +#' Constructs a simple Gaussian model where the state dynamics +#' follow an AR(1) process. +#' +#' @inheritParams ar1_ng +#' @param sd_y A prior for the standard deviation of observation equation. +#' @return An object of class \code{ar1_lg}. #' @export #' @rdname ar1_lg #' @examples -#' model <- ar1_lg(BJsales, rho = uniform(0.5,-1,1), -#' sigma = halfnormal(1, 10), mu = normal(200, 200, 100), -#' sd_y = halfnormal(1, 10)) +#' set.seed(1) +#' mu <- 2 +#' rho <- 0.7 +#' sd_y <- 0.1 +#' sigma <- 0.5 +#' beta <- -1 +#' x <- rnorm(30) +#' z <- y <- numeric(30) +#' z[1] <- rnorm(1, mu, sigma / sqrt(1 - rho^2)) +#' y[1] <- rnorm(1, beta * x[1] + z[1], sd_y) +#' for(i in 2:30) { +#' z[i] <- rnorm(1, mu * (1 - rho) + rho * z[i - 1], sigma) +#' y[i] <- rnorm(1, beta * x[i] + z[i], sd_y) +#' } +#' model <- ar1_lg(y, rho = uniform(0.5, -1, 1), +#' sigma = halfnormal(1, 10), mu = normal(0, 0, 1), +#' sd_y = halfnormal(1, 10), +#' xreg = x, beta = normal(0, 0, 1)) #' out <- run_mcmc(model, iter = 2e4) #' summary(out, return_se = TRUE) +#' ar1_lg <- function(y, rho, sigma, mu, sd_y, beta, xreg = NULL) { - check_y(y) + y <- check_y(y) n <- length(y) - if (is.null(xreg)) { - xreg <- matrix(0, 0, 0) - coefs <- numeric(0) - beta <- NULL - } else { - - if (missing(beta) || is.null(beta)) { - stop("No prior defined for beta. ") - } - if(!is_prior(beta) && !is_prior_list(beta)) { - stop("Prior for beta must be of class 'bssm_prior' or 'bssm_prior_list.") - } - n <- length(y) - if (is.null(dim(xreg)) && length(xreg) == n) { - xreg <- matrix(xreg, n, 1) - } - check_xreg(xreg, n) - nx <- ncol(xreg) - if (nx == 1 && is_prior_list(beta)) beta <- beta[[1]] - if(nx > 1) { - coefs <- sapply(beta, "[[", "init") - } else { - coefs <- beta$init - } - check_beta(coefs, nx) - - if (nx > 0 && is.null(colnames(xreg))) { - colnames(xreg) <- paste0("coef_",1:ncol(xreg)) - } - names(coefs) <- colnames(xreg) - - } - + regression_part <- create_regression(beta, xreg, n) check_rho(rho$init) check_sd(sigma$init, "rho") @@ -1567,72 +1441,111 @@ ar1_lg <- function(y, rho, sigma, mu, sd_y, beta, xreg = NULL) { rownames(T) <- colnames(T) <- rownames(R) <- "signal" - if(ncol(xreg) > 1) { - priors <- c(list(rho, sigma, mu, sd_y), beta) + if(ncol(regression_part$xreg) > 1) { + priors <- c(list(rho, sigma, mu, sd_y), regression_part$beta) } else { - priors <- list(rho, sigma, mu, sd_y, beta) + priors <- list(rho, sigma, mu, sd_y, regression_part$beta) } names(priors) <- - c("rho", "sigma", "mu", "sd_y", names(coefs)) - priors <- priors[sapply(priors, is_prior)] + c("rho", "sigma", "mu", "sd_y", names(regression_part$coefs)) + priors <- priors[vapply(priors, is_prior, TRUE)] D <- matrix(0) - theta <- if (length(priors) > 0) sapply(priors, "[[", "init") else numeric(0) + theta <- if (length(priors) > 0) { + vapply(priors, "[[", "init", FUN.VALUE = 1) + } else numeric(0) priors <- combine_priors(priors) structure(list(y = as.ts(y), Z = Z, H = H, T = T, R = R, - a1 = a1, P1 = P1, xreg = xreg, beta = coefs, + a1 = a1, P1 = P1, + xreg = regression_part$xreg, beta = regression_part$coefs, D = D, C = C, mu_est = mu_est, sd_y_est = sd_y_est, - prior_distributions = priors$prior_distribution, prior_parameters = priors$parameters, - theta = theta, + prior_distributions = priors$prior_distribution, + prior_parameters = priors$parameters, theta = theta, max_iter = 100, conv_tol = 1e-8), - class = c("ar1_lg", "ssm_ulg", "gaussian")) + class = c("ar1_lg", "ssm_ulg", "lineargaussian", "bssm_model")) } #' #' General multivariate nonlinear Gaussian state space models #' -#' Constructs an object of class \code{ssm_nlg} by defining the corresponding terms -#' of the observation and state equation. +#' Constructs an object of class \code{ssm_nlg} by defining the corresponding +#' terms of the observation and state equation. #' #' The nonlinear Gaussian model is defined as #' -#' \deqn{y_t = Z(t, \alpha_t, \theta) + H(t, \theta) \epsilon_t, (\textrm{observation equation})} -#' \deqn{\alpha_{t+1} = T(t, \alpha_t, \theta) + R(t, \theta)\eta_t, (\textrm{transition equation})} +#' \deqn{y_t = Z(t, \alpha_t, \theta) + H(t, \theta) \epsilon_t, +#' (\textrm{observation equation})} +#' \deqn{\alpha_{t+1} = T(t, \alpha_t, \theta) + R(t, \theta)\eta_t, +#' (\textrm{transition equation})} #' #' where \eqn{\epsilon_t \sim N(0, I_p)}, \eqn{\eta_t \sim N(0, I_m)} and #' \eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other, and functions -#' \eqn{Z, H, T, R} can depend on \eqn{\alpha_t} and parameter vector \eqn{\theta}. +#' \eqn{Z, H, T, R} can depend on \eqn{\alpha_t} and parameter vector +#' \eqn{\theta}. #' #' Compared to other models, these general models need a bit more effort from #' the user, as you must provide the several small C++ snippets which define the -#' model structure. See examples in the vignette. +#' model structure. See examples in the vignette and \code{cpp_example_model}. #' -#' @param y Observations as multivariate time series (or matrix) of length \eqn{n}. -#' @param Z,H,T,R An external pointers for the C++ functions which -#' define the corresponding model functions. -#' @param Z_gn,T_gn An external pointers for the C++ functions which -#' define the gradients of the corresponding model functions. -#' @param a1 Prior mean for the initial state as a vector of length m. -#' @param P1 Prior covariance matrix for the initial state as m x m matrix. +#' @param y Observations as multivariate time series (or matrix) of length +#' \eqn{n}. +#' @param Z,H,T,R An external pointers (object of class \code{externalptr}) +#' for the C++ functions which define the corresponding model functions. +#' @param Z_gn,T_gn An external pointers (object of class \code{externalptr}) +#' for the C++ functions which define the gradients of the corresponding model +#' functions. +#' @param a1 Prior mean for the initial state as object of class +#' \code{externalptr} +#' @param P1 Prior covariance matrix for the initial state as object of class +#' \code{externalptr} #' @param theta Parameter vector passed to all model functions. -#' @param known_params Vector of known parameters passed to all model functions. -#' @param known_tv_params Matrix of known parameters passed to all model functions. -#' @param n_states Number of states in the model. -#' @param n_etas Dimension of the noise term of the transition equation. -#' @param log_prior_pdf An external pointer for the C++ function which +#' @param known_params A vector of known parameters passed to all model +#' functions. +#' @param known_tv_params A matrix of known parameters passed to all model +#' functions. +#' @param n_states Number of states in the model (positive integer). +#' @param n_etas Dimension of the noise term of the transition equation +#' (positive integer). +#' @param log_prior_pdf An external pointer (object of class +#' \code{externalptr}) for the C++ function which #' computes the log-prior density given theta. -#' @param time_varying Optional logical vector of length 4, denoting whether the values of +#' @param time_varying Optional logical vector of length 4, denoting whether +#' the values of #' Z, H, T, and R vary with respect to time variable (given identical states). #' If used, this can speed up some computations. -#' @param state_names Names for the states. -#' @return Object of class \code{ssm_nlg}. +#' @param state_names A character vector containing names for the states. +#' @return An object of class \code{ssm_nlg}. #' @export +#' @examples +#' \donttest{ # Takes a while on CRAN +#' set.seed(1) +#' n <- 50 +#' x <- y <- numeric(n) +#' y[1] <- rnorm(1, exp(x[1]), 0.1) +#' for(i in 1:(n-1)) { +#' x[i+1] <- rnorm(1, sin(x[i]), 0.1) +#' y[i+1] <- rnorm(1, exp(x[i+1]), 0.1) +#' } +#' +#' pntrs <- cpp_example_model("nlg_sin_exp") +#' +#' model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, +#' Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, +#' Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, +#' theta = c(log_H = log(0.1), log_R = log(0.1)), +#' log_prior_pdf = pntrs$log_prior_pdf, +#' n_states = 1, n_etas = 1, state_names = "state") +#' +#' out <- ekf(model_nlg, iekf_iter = 100) +#' ts.plot(cbind(x, out$at[1:n], out$att[1:n]), col = 1:3) +#' } ssm_nlg <- function(y, Z, H, T, R, Z_gn, T_gn, a1, P1, theta, known_params = NA, known_tv_params = matrix(NA), n_states, n_etas, - log_prior_pdf, time_varying = rep(TRUE, 4), state_names = paste0("state",1:n_states)) { + log_prior_pdf, time_varying = rep(TRUE, 4), + state_names = paste0("state", 1:n_states)) { if (is.null(dim(y))) { dim(y) <- c(length(y), 1) @@ -1641,6 +1554,11 @@ ssm_nlg <- function(y, Z, H, T, R, Z_gn, T_gn, a1, P1, theta, if(missing(n_etas)) { n_etas <- n_states } + n_states <- as.integer(n_states) + n_etas <- as.integer(n_etas) + + theta <- check_theta(theta) + structure(list(y = as.ts(y), Z = Z, H = H, T = T, R = R, Z_gn = Z_gn, T_gn = T_gn, a1 = a1, P1 = P1, theta = theta, log_prior_pdf = log_prior_pdf, known_params = known_params, @@ -1649,7 +1567,7 @@ ssm_nlg <- function(y, Z, H, T, R, Z_gn, T_gn, a1, P1, theta, time_varying = time_varying, state_names = state_names, max_iter = 100, conv_tol = 1e-8), - class = "ssm_nlg") + class = c("ssm_nlg", "bssm_model")) } #' @@ -1660,25 +1578,30 @@ ssm_nlg <- function(y, Z, H, T, R, Z_gn, T_gn, a1, P1, theta, #' as well as the log-density of observation equation. We assume that the #' observations are measured at integer times (missing values are allowed). #' -#' As in case of \code{ssm_nlg} models, these general models need a bit more effort from -#' the user, as you must provide the several small C++ snippets which define the -#' model structure. See vignettes for an example. +#' As in case of \code{ssm_nlg} models, these general models need a bit more +#' effort from the user, as you must provide the several small C++ snippets +#' which define the model structure. See vignettes for an example and +#' \code{cpp_example_model}. #' -#' @param y Observations as univariate time series (or vector) of length \eqn{n}. -#' @param drift,diffusion,ddiffusion An external pointers for the C++ functions which +#' @param y Observations as univariate time series (or vector) of length +#' \eqn{n}. +#' @param drift,diffusion,ddiffusion An external pointers for the C++ functions +#' which #' define the drift, diffusion and derivative of diffusion functions of SDE. #' @param obs_pdf An external pointer for the C++ function which -#' computes the observational log-density given the the states and parameter vector theta. +#' computes the observational log-density given the the states and parameter +#' vector theta. #' @param prior_pdf An external pointer for the C++ function which #' computes the prior log-density given the parameter vector theta. #' @param theta Parameter vector passed to all model functions. #' @param x0 Fixed initial value for SDE at time 0. #' @param positive If \code{TRUE}, positivity constraint is #' forced by \code{abs} in Milstein scheme. -#' @return Object of class \code{ssm_sde}. +#' @return An object of class \code{ssm_sde}. #' @export #' @examples -#' \dontrun{ +#' +#' \donttest{ # Takes a while on CRAN #' library("sde") #' set.seed(1) #' # theta_0 = rho = 0.5 @@ -1690,9 +1613,8 @@ ssm_nlg <- function(y, Z, H, T, R, Z_gn, T_gn, a1, P1, theta, #' sigma.x = expression(0)) #' y <- rpois(50, exp(x[-1])) #' -#' # Template can be found in the vignette -#' Rcpp::sourceCpp("ssm_sde_template.cpp") -#' pntrs <- create_xptrs() +#' # source c++ snippets +#' pntrs <- cpp_example_model("sde_poisson_OU") #' #' sde_model <- ssm_sde(y, pntrs$drift, pntrs$diffusion, #' pntrs$ddiffusion, pntrs$obs_density, pntrs$prior, @@ -1705,23 +1627,24 @@ ssm_nlg <- function(y, Z, H, T, R, Z_gn, T_gn, a1, P1, theta, #' est$alphahat + 2*sqrt(c(est$Vt))), #' col = c(2, 1, 1, 1), lty = c(1, 1, 2, 2)) #' +#' #' # Takes time with finer mesh, parallelization with IS-MCMC helps a lot #' out <- run_mcmc(sde_model, L_c = 4, L_f = 8, #' particles = 50, iter = 2e4, #' threads = 4L) -#' #' } +#' ssm_sde <- function(y, drift, diffusion, ddiffusion, obs_pdf, prior_pdf, theta, x0, positive) { - check_y(y) - n <- length(y) - + y <- check_y(y) + theta <- check_theta(theta) structure(list(y = as.ts(y), drift = drift, diffusion = diffusion, ddiffusion = ddiffusion, obs_pdf = obs_pdf, prior_pdf = prior_pdf, theta = theta, x0 = x0, - positive = positive, state_names = "x"), class = "ssm_sde") + positive = positive, state_names = "x"), + class = c("ssm_sde", "bssm_model")) } diff --git a/R/particle_smoother.R b/R/particle_smoother.R index 324a7563..e70da937 100644 --- a/R/particle_smoother.R +++ b/R/particle_smoother.R @@ -1,49 +1,74 @@ #' Particle Smoothing #' #' Function \code{particle_smoother} performs particle smoothing -#' based on either bootstrap particle filter [1], \eqn{\psi}-auxiliary particle filter (\eqn{\psi}-APF) [2], -#' or extended Kalman particle filter [3] (or its iterated version [4]). -#' The smoothing phase is based on the filter-smoother algorithm by [5]. +#' based on either bootstrap particle filter (Gordon et al. 1993), +#' \eqn{\psi}-auxiliary particle filter (\eqn{\psi}-APF) (Vihola et al. 2020), +#' extended Kalman particle filter (Van Der Merwe et al. 2001), +#' or its version based on iterated EKF (Jazwinski, 1970). +#' The smoothing phase is based on the filter-smoother algorithm by +#' Kitagawa (1996). #' #' See one of the vignettes for \eqn{\psi}-APF in case of nonlinear models. #' #' @importFrom stats cov -#' @param model Model. -#' @param particles Number of samples for particle filter. +#' @inheritParams bootstrap_filter #' @param method Choice of particle filter algorithm. #' For Gaussian and non-Gaussian models with linear dynamics, -#' options are \code{"bsf"} (bootstrap particle filter, default for non-linear models) +#' options are \code{"bsf"} (bootstrap particle filter, default for +#' non-linear models) #' and \code{"psi"} (\eqn{\psi}-APF, the default for other models), and -#' for non-linear models options \code{"ekf"} (extended Kalman particle filter) +#' for non-linear models option \code{"ekf"} (extended Kalman particle filter) #' is also available. -#' @param max_iter Maximum number of iterations used in Gaussian approximation. Used \eqn{\psi}-APF. -#' @param conv_tol Tolerance parameter used in Gaussian approximation. Used \eqn{\psi}-APF. -#' @param iekf_iter If zero (default), first approximation for non-linear -#' Gaussian models is obtained from extended Kalman filter. If -#' \code{iekf_iter > 0}, iterated extended Kalman filter is used with -#' \code{iekf_iter} iterations. -#' @param seed Seed for RNG. +#' @param particles Number of particles as a positive integer. Suitable values +#' depend on the model, the data, and the chosen algorithm. While larger values +#' provide more accurate estimates, the run time also increases with respect to +#' the number of particles, so it is generally a good idea to test the filter +#' first with a small number of particles, e.g., less than 100. +#' @param max_iter Maximum number of iterations used in Gaussian approximation, +#' as a positive integer. +#' Default is 100 (although typically only few iterations are needed). +#' @param conv_tol Positive tolerance parameter used in Gaussian approximation. +#' Default is 1e-8. +#' @param iekf_iter Non-negative integer. If zero (default), first +#' approximation for non-linear Gaussian models is obtained from extended +#' Kalman filter. If \code{iekf_iter > 0}, iterated extended Kalman filter is +#' used with \code{iekf_iter} iterations. #' @param ... Ignored. -#' @return List with samples (\code{alpha}) from the smoothing distribution and corresponding weights (\code{weights}), -#' as well as smoothed means and covariances (\code{alphahat} and \code{Vt}) of the states and +#' @return List with samples (\code{alpha}) from the smoothing distribution +#' and corresponding weights (\code{weights}), +#' as well as smoothed means and covariances (\code{alphahat} and \code{Vt}) +#' of the states and #' estimated log-likelihood (\code{logLik}). #' @references -#' [1] Gordon, N. J., Salmond, D. J., & Smith, A. F. M. (1993). -#' Novel approach to nonlinear/non-Gaussian Bayesian state estimation. IEE Proceedings-F, 140, 107–113. -#' [2] Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -#' Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 -#' [3] Van Der Merwe, R., Doucet, A., De Freitas, N., & Wan, E. A. (2001). The unscented particle filter. -#' In Advances in neural information processing systems (pp. 584-590). -#' [4] Jazwinski, A. 1970. Stochastic Processes and Filtering Theory. Academic Press. -#' [5] Kitagawa, G. (1996). Monte Carlo filter and smoother for non-Gaussian nonlinear state space models. -#' Journal of Computational and Graphical Statistics, 5, 1–25. +#' Gordon, NJ, Salmond, DJ, Smith, AFM (1993). +#' Novel approach to nonlinear/non-Gaussian Bayesian state estimation. +#' IEE Proceedings-F, 140, 107-113. +#' https://doi.org/10.1049/ip-f-2.1993.0015 +#' +#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators +#' based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 2020; 1-38. +#' https://doi.org/10.1111/sjos.12492 +#' +#' Van Der Merwe, R, Doucet, A, De Freitas, N, Wan, EA (2001). +#' The unscented particle filter. +#' In Advances in neural information processing systems, p 584-590. +#' +#' Jazwinski, A 1970. Stochastic Processes and Filtering Theory. +#' Academic Press. +#' +#' Kitagawa, G (1996). Monte Carlo filter and smoother for non-Gaussian +#' nonlinear state space models. +#' Journal of Computational and Graphical Statistics, 5, 1-25. +#' https://doi.org/10.2307/1390750 +#' #' @export #' @rdname particle_smoother particle_smoother <- function(model, particles, ...) { UseMethod("particle_smoother", model) } -#' @method particle_smoother gaussian +#' @method particle_smoother lineargaussian #' @export #' @rdname particle_smoother #' @examples @@ -53,30 +78,47 @@ particle_smoother <- function(model, particles, ...) { #' model <- ssm_ulg(y, Z = 1, T = 1, R = 1, H = 1, P1 = 1) #' system.time(out <- particle_smoother(model, particles = 1000)) #' # same with simulation smoother: -#' system.time(out2 <- sim_smoother(model, particles = 1000, use_antithetic = TRUE)) +#' system.time(out2 <- sim_smoother(model, particles = 1000, +#' use_antithetic = TRUE)) #' ts.plot(out$alphahat, rowMeans(out2), col = 1:2) #' -particle_smoother.gaussian <- function(model, particles, method = "psi", +particle_smoother.lineargaussian <- function(model, particles, method = "psi", seed = sample(.Machine$integer.max, size = 1), ...) { - if(missing(particles)) { + check_missingness(model) + + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } - if(method == "psi") { + particles <- check_intmax(particles, "particles") + + nsamples <- ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + length(model$a1) * particles + if (particles > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + + if (method == "psi") { out <- list() - out$alpha <- gaussian_psi_smoother(model, particles, seed, model_type(model)) + out$alpha <- gaussian_psi_smoother(model, particles, seed, + model_type(model)) out$alphahat <- t(apply(out$alpha, 1:2, mean)) - if(ncol(out$alphahat) == 1L) { - out$Vt <- array(apply(out$alpha[1, , ], 1, var), c(1, 1, nrow(out$alphahat))) + if (ncol(out$alphahat) == 1L) { + out$Vt <- array(apply(out$alpha[1, , ], 1, var), + c(1, 1, nrow(out$alphahat))) } else { - out$Vt <- array(NA, c(ncol(out$alphahat), ncol(out$alphahat), nrow(out$alphahat))) - for(i in 1:nrow(out$alphahat)) { - out$Vt[,, i] <- cov(t(out$alpha[,i,])) + out$Vt <- array(NA, c(ncol(out$alphahat), ncol(out$alphahat), + nrow(out$alphahat))) + for (i in seq_len(nrow(out$alphahat))) { + out$Vt[, , i] <- cov(t(out$alpha[, i, ])) } } } else { @@ -100,23 +142,35 @@ particle_smoother.nongaussian <- function(model, particles, seed = sample(.Machine$integer.max, size = 1), max_iter = 100, conv_tol = 1e-8, ...) { - if(missing(particles)) { + check_missingness(model) + + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } + particles <- check_intmax(particles, "particles") + nsamples <- ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + length(model$a1) * particles + if (particles > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) - method <- match.arg(method, c("bsf", "psi")) + model$max_iter <- check_intmax(max_iter, "max_iter", positive = FALSE) + model$conv_tol <- check_positive_real(conv_tol, "conv_tol") - model$max_iter <- max_iter - model$conv_tol <- conv_tol + method <- match.arg(tolower(method), c("bsf", "psi")) + model$distribution <- pmatch(model$distribution, c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), duplicates.ok = TRUE) - 1 - if(method == "psi") { + if (method == "psi") { out <- psi_smoother(model, particles, seed, model_type(model)) } else { out <- bsf_smoother(model, particles, seed, FALSE, model_type(model)) @@ -139,15 +193,30 @@ particle_smoother.ssm_nlg <- function(model, particles, seed = sample(.Machine$integer.max, size = 1), max_iter = 100, conv_tol = 1e-8, iekf_iter = 0, ...) { - if(missing(particles)) { + check_missingness(model) + + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } + particles <- check_intmax(particles, "particles") - method <- match.arg(method, c("bsf", "psi", "ekf")) + nsamples <- ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + model$n_states * particles + if (particles > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + max_iter <- check_intmax(max_iter, "max_iter", positive = FALSE) + conv_tol <- check_positive_real(conv_tol, "conv_tol") + iekf_iter <- check_intmax(iekf_iter, "iekf_iter", positive = FALSE) + + method <- match.arg(tolower(method), c("bsf", "psi", "ekf")) out <- switch(method, psi = psi_smoother_nlg(t(model$y), model$Z, model$H, model$T, @@ -181,21 +250,30 @@ particle_smoother.ssm_nlg <- function(model, particles, #' @rdname particle_smoother #' @method particle_smoother ssm_sde -#' @param L Integer defining the discretization level. +#' @param L Positive integer defining the discretization level for SDE model. #' @export particle_smoother.ssm_sde <- function(model, particles, L, seed = sample(.Machine$integer.max, size = 1), ...) { - if(L < 1) stop("Discretization level L must be larger than 0.") + check_missingness(model) + + if (L < 1) stop("Discretization level L must be larger than 0.") - if(missing(particles)) { + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } - + particles <- check_intmax(particles, "particles") + nsamples <- length(model$y) * particles + if (particles > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) out <- bsf_smoother_sde(model$y, model$x0, model$positive, model$drift, model$diffusion, model$ddiffusion, model$prior_pdf, model$obs_pdf, model$theta, diff --git a/R/plot_mcmc.R b/R/plot_mcmc.R new file mode 100644 index 00000000..241a7b63 --- /dev/null +++ b/R/plot_mcmc.R @@ -0,0 +1,38 @@ +#' Trace and Density Plots for `mcmc_output` +#' +#' Plots the trace and density plots of the hyperparameters theta from the MCMC +#' run by \code{\link{run_mcmc}}. +#' +#' For further visualization (of the states), you can extract the posterior +#' samples with `as.data.frame` and `as_draws` methods to be used for example +#' with the `bayesplot` or `ggplot2` packages. +#' +#' +#' @note For IS-MCMC, these plots correspond to the approximate (non-weighted) +#' samples +#' . +#' @method plot mcmc_output +#' @importFrom bayesplot mcmc_combo +#' @param x Object of class \code{mcmc_output} from \code{\link{run_mcmc}}. +#' @param ... Further arguments to [bayesplot::mcmc_combo]. +#' @return The output object from [bayesplot::mcmc_combo]. +#' @seealso \code{\link{check_diagnostics}} for a quick diagnostics statistics +#' of the model. +#' @export +#' @examples +#' data("negbin_model") +#' # Note the very small number of iterations, so the plots look bad +#' plot(negbin_model) +plot.mcmc_output <- function(x, ...) { + + # suppress the duplicate warning about the IS-MCMC + out <- suppressWarnings(as_draws(x, states = 0)) + + if (x$mcmc_type %in% paste0("is", 1:3)) { + warning("Input is based on a IS-weighted MCMC, the plots ", + "correspond to the approximate MCMC.") + # remove the weight variable + out <- out[, -1] + } + mcmc_combo(out, ...) +} diff --git a/R/post_correction.R b/R/post_correction.R index 26cac129..3e060a15 100644 --- a/R/post_correction.R +++ b/R/post_correction.R @@ -1,39 +1,52 @@ -# Get MAP estimate of theta +#' Get MAP estimate of theta +#' @param x Object of class \code{mcmc_output} or any other list style object +#' which has matrix theta (where each row corresponds to one iteration) and +#' vector \code{posterior}, +#' @return A vector containing theta corresponding to maximum log-posterior +#' value of the posterior sample. +#' @noRd get_map <- function(x) { - x$theta[which.max(x$posterior),] + x$theta[which.max(x$posterior), ] } #' Suggest Number of Particles for \eqn{\psi}-APF Post-correction #' -#' Function \code{estimate_N} estimates suitable number particles needed for accurate -#' post-correction of approximate MCMC +#' Function \code{estimate_N} estimates suitable number particles needed for +#' accurate post-correction of approximate MCMC. #' #' Function \code{suggest_N} estimates the standard deviation of the #' logarithm of the post-correction weights at approximate MAP of theta, #' using various particle sizes and suggest smallest number of particles -#' which still leads standard deviation less than 1. Similar approach was suggested in -#' the context of pseudo-marginal MCMC by Doucet et al. (2015), but see also -#' Section 10.3 in Vihola et al (2020). +#' which still leads standard deviation less than 1. Similar approach was +#' suggested in the context of pseudo-marginal MCMC by Doucet et al. (2015), +#' but see also Section 10.3 in Vihola et al (2020). #' #' @param model Model of class \code{nongaussian} or \code{ssm_nlg}. -#' @param mcmc_output An output from \code{run_mcmc} used to compute the MAP estimate of theta. -#' While the intended use assumes this is from approximate MCMC, it is not actually checked, i.e., -#' it is also possible to input previous (asymptotically) exact output. -#' @param candidates Vector containing the candidate number of particles to test. Default -#' is \code{seq(10, 100, by = 10)}. -#' @param replications How many replications should be used for computing the standard deviations? -#' Default is 100. -#' @param seed Seed for the random number generator. -#' @return List with suggested number of particles \code{N} and matrix containing -#' estimated standard deviations of the log-weights and corresponding number of particles. -#' @references A. Doucet, M. K. Pitt, G. Deligiannidis, R. Kohn, -#' Efficient implementation of Markov chain Monte Carlo when using an unbiased likelihood estimator, -#' Biometrika, Volume 102, Issue 2, 2015, Pages 295–313, https://doi.org/10.1093/biomet/asu075 -#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -#' Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +#' @param theta A vector of theta corresponding to the model, at which point +#' the standard deviation of the log-likelihood is computed. Typically MAP +#' estimate from the (approximate) MCMC run. Can also be an output from +#' \code{run_mcmc} which is then used to compute the MAP +#' estimate of theta. +#' @param candidates A vector of positive integers containing the candidate +#' number of particles to test. Default is \code{seq(10, 100, by = 10)}. +#' @param replications Positive integer, how many replications should be used +#' for computing the standard deviations? Default is 100. +#' @param seed Seed for the C++ RNG (positive integer). +#' @return List with suggested number of particles \code{N} and matrix +#' containing estimated standard deviations of the log-weights and +#' corresponding number of particles. +#' @references +#' Doucet, A, Pitt, MK, Deligiannidis, G, Kohn, R (2015). +#' Efficient implementation of Markov chain Monte Carlo when using an +#' unbiased likelihood estimator, Biometrika, 102(2) p. 295-313, +#' https://doi.org/10.1093/biomet/asu075 +#' +#' Vihola, M, Helske, J, Franks, J (2020). Importance sampling type estimators +#' based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 #' @export #' @examples -#' \dontrun{ +#' #' set.seed(1) #' n <- 300 #' x1 <- sin((2 * pi / 12) * 1:n) @@ -41,7 +54,7 @@ get_map <- function(x) { #' alpha <- numeric(n) #' alpha[1] <- 0 #' rho <- 0.7 -#' sigma <- 2 +#' sigma <- 1.2 #' mu <- 1 #' for(i in 2:n) { #' alpha[i] <- rnorm(1, mu * (1 - rho) + rho * alpha[i-1], sigma) @@ -52,30 +65,58 @@ get_map <- function(x) { #' ts.plot(y / u) #' #' model <- ar1_ng(y, distribution = "binomial", -#' rho = uniform(0.5, -1, 1), sigma = gamma(1, 2, 0.001), +#' rho = uniform(0.5, -1, 1), sigma = gamma_prior(1, 2, 0.001), #' mu = normal(0, 0, 10), #' xreg = cbind(x1,x2), beta = normal(c(0, 0), 0, 5), #' u = u) #' -#' out_approx <- run_mcmc(model, mcmc_type = "approx", -#' iter = 5000) -#' -#' estN <- suggest_N(model, out_approx, candidates = seq(10, 50, by = 10)) +#' # theta from earlier approximate MCMC run +#' # out_approx <- run_mcmc(model, mcmc_type = "approx", +#' # iter = 5000) +#' # theta <- out_approx$theta[which.max(out_approx$posterior), ] +#' +#' theta <- c(rho = 0.64, sigma = 1.16, mu = 1.1, x1 = 0.56, x2 = 1.28) +#' +#' estN <- suggest_N(model, theta, candidates = seq(10, 50, by = 10), +#' replications = 50, seed = 1) #' plot(x = estN$results$N, y = estN$results$sd, type = "b") #' estN$N -#' } -suggest_N <- function(model, mcmc_output, candidates = seq(10, 100, by = 10), - replications = 100, seed = sample(.Machine$integer.max, size = 1)) { +#' +suggest_N <- function(model, theta, + candidates = seq(10, 100, by = 10), replications = 100, + seed = sample(.Machine$integer.max, size = 1)) { + + check_missingness(model) + + replications <- check_intmax(replications, "replications", max = 1000) + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) - if (!inherits(mcmc_output, "mcmc_output")) stop("Object 'mcmc_output' is not valid output from 'run_mcmc'.") - theta <- get_map(mcmc_output) + if (!test_integerish(candidates, lower = 1, any.missing = FALSE, + min.len = 1)) { + stop("Argument 'candidates' should be vector of positive integers. ") + } + if (max(candidates) > 1e5) + stop(paste("I don't believe you want to use over 1e5 particles", + "If you really do, please file an issue at Github.", sep = " ")) + + if (missing(theta) | (!is.vector(theta) & !inherits(theta, "mcmc_output"))) { + stop("'theta' should be either a vector or object of class 'mcmc_output'.") + } + if (inherits(theta, "mcmc_output")) { + theta <- get_map(theta) + } else { + if (length(theta) != length(model$theta)) { + stop("Length of 'theta' does not match length of 'model$theta'.") + } + } if (inherits(model, "nongaussian")) { model$distribution <- pmatch(model$distribution, - c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), - duplicates.ok = TRUE) - 1 + c("svm", "poisson", "binomial", "negative binomial", "gamma", + "gaussian"), duplicates.ok = TRUE) - 1 - out <- suggest_n_nongaussian(model, theta, candidates, replications, seed, model_type(model)) + out <- suggest_n_nongaussian(model, theta, candidates, replications, seed, + model_type(model)) } else { if (inherits(model, "ssm_nlg")) { out <- suggest_n_nonlinear(t(model$y), model$Z, model$H, model$T, @@ -84,39 +125,55 @@ suggest_N <- function(model, mcmc_output, candidates = seq(10, 100, by = 10), model$known_tv_params, model$n_states, model$n_etas, as.integer(model$time_varying), theta, candidates, replications, seed) - } else stop("Function 'suggest_N' is only available for models of class 'nongaussian' and 'nlg_ssm'.") + } else + stop(paste("Function 'suggest_N' is only available for models of", + "class 'nongaussian' and 'nlg_ssm'.", sep = " ")) } - list(N = candidates[which(out < 1)[1]], results = data.frame(N = candidates, sd = out)) + list(N = candidates[which(out < 1)[1]], results = data.frame(N = candidates, + sd = out)) } #' Run Post-correction for Approximate MCMC using \eqn{\psi}-APF #' -#' Function \code{post_correct} updates previously obtained approximate MCMC output -#' with post-correction weights leading to asymptotically exact weighted posterior, -#' and returns updated MCMC output where components \code{weights}, \code{posterior}, -#' \code{alpha}, \code{alphahat}, and \code{Vt} are updated (depending on the original output type). +#' Function \code{post_correct} updates previously obtained approximate MCMC +#' output with post-correction weights leading to asymptotically exact +#' weighted posterior, and returns updated MCMC output where components +#' \code{weights}, \code{posterior}, \code{alpha}, \code{alphahat}, and +#' \code{Vt} are updated (depending on the original output type). #' #' @param model Model of class \code{nongaussian} or \code{ssm_nlg}. -#' @param mcmc_output An output from \code{run_mcmc} used to compute the MAP estimate of theta. -#' While the intended use assumes this is from approximate MCMC, it is not actually checked, i.e., -#' it is also possible to input previous (asymptotically) exact output. -#' @param particles Number of particles for \eqn{\psi}-APF. -#' @param threads Number of parallel threads. +#' @param mcmc_output An output from \code{run_mcmc} used to compute the MAP +#' estimate of theta. +#' While the intended use assumes this is from approximate MCMC, it is not +#' actually checked, i.e., it is also possible to input previous +#' (asymptotically) exact output. +#' @param particles Number of particles for \eqn{\psi}-APF (positive integer). +#' Suitable values depend on the model and the data, but often relatively +#' small value less than say 50 is enough. See also \code{suggest_N} +#' @param threads Number of parallel threads (positive integer, default is 1). #' @param is_type Type of IS-correction. Possible choices are -#'\code{"is3"} for simple importance sampling (weight is computed for each MCMC iteration independently), +#'\code{"is3"} for simple importance sampling (weight is computed for each +#'MCMC iteration independently), #' \code{"is2"} for jump chain importance sampling type weighting (default), or -#' \code{"is1"} for importance sampling type weighting where the number of particles used for -#' weight computations is proportional to the length of the jump chain block. -#' @param seed Seed for the random number generator. -#' @return List with suggested number of particles \code{N} and matrix containing -#' estimated standard deviations of the log-weights and corresponding number of particles. -#' @references A. Doucet, M. K. Pitt, G. Deligiannidis, R. Kohn, -#' Efficient implementation of Markov chain Monte Carlo when using an unbiased likelihood estimator, -#' Biometrika, Volume 102, Issue 2, 2015, Pages 295–313, https://doi.org/10.1093/biomet/asu075 -#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -#' Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +#' \code{"is1"} for importance sampling type weighting where the number of +#' particles used forweight computations is proportional to the length of the +#' jump chain block. +#' @param seed Seed for the C++ RNG (positive integer). +#' @return The original object of class \code{mcmc_output} with updated +#' weights, log-posterior values and state samples or summaries (depending on +#' the \code{mcmc_output$mcmc_type}). +#' +#' @references +#' Doucet A, Pitt M K, Deligiannidis G, Kohn R (2018). +#' Efficient implementation of Markov chain Monte Carlo when using an unbiased +#' likelihood estimator. Biometrika, 102, 2, 295-313, +#' https://doi.org/10.1093/biomet/asu075 +#' +#' Vihola M, Helske J, Franks J (2020). Importance sampling type estimators +#' based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 #' @export #' @examples -#' \dontrun{ +#' \donttest{ #' set.seed(1) #' n <- 300 #' x1 <- sin((2 * pi / 12) * 1:n) @@ -135,7 +192,7 @@ suggest_N <- function(model, mcmc_output, candidates = seq(10, 100, by = 10), #' ts.plot(y / u) #' #' model <- ar1_ng(y, distribution = "binomial", -#' rho = uniform(0.5, -1, 1), sigma = gamma(1, 2, 0.001), +#' rho = uniform(0.5, -1, 1), sigma = gamma_prior(1, 2, 0.001), #' mu = normal(0, 0, 10), #' xreg = cbind(x1,x2), beta = normal(c(0, 0), 0, 5), #' u = u) @@ -153,34 +210,34 @@ suggest_N <- function(model, mcmc_output, candidates = seq(10, 100, by = 10), #' # latent state #' library("dplyr") #' library("ggplot2") -#' state_approx <- as.data.frame(out_approx, variable = "states") %>% -#' group_by(time) %>% +#' state_approx <- as.data.frame(out_approx, variable = "states") |> +#' group_by(time) |> #' summarise(mean = mean(value)) #' -#' state_exact <- as.data.frame(out_is2, variable = "states") %>% -#' group_by(time) %>% +#' state_exact <- as.data.frame(out_is2, variable = "states") |> +#' group_by(time) |> #' summarise(mean = weighted.mean(value, weight)) #' #' dplyr::bind_rows(approx = state_approx, -#' exact = state_exact, .id = "method") %>% -#' filter(time > 200) %>% +#' exact = state_exact, .id = "method") |> +#' filter(time > 200) |> #' ggplot(aes(time, mean, colour = method)) + #' geom_line() + #' theme_bw() #' #' # posterior means #' p_approx <- predict(out_approx, model, type = "mean", -#' nsim = 1000, future = FALSE) %>% -#' group_by(time) %>% +#' nsim = 1000, future = FALSE) |> +#' group_by(time) |> #' summarise(mean = mean(value)) #' p_exact <- predict(out_is2, model, type = "mean", -#' nsim = 1000, future = FALSE) %>% -#' group_by(time) %>% +#' nsim = 1000, future = FALSE) |> +#' group_by(time) |> #' summarise(mean = mean(value)) #' -#' dplyr:: bind_rows(approx = p_approx, -#' exact = p_exact, .id = "method") %>% -#' filter(time > 200) %>% +#' dplyr::bind_rows(approx = p_approx, +#' exact = p_exact, .id = "method") |> +#' filter(time > 200) |> #' ggplot(aes(time, mean, colour = method)) + #' geom_line() + #' theme_bw() @@ -188,19 +245,29 @@ suggest_N <- function(model, mcmc_output, candidates = seq(10, 100, by = 10), post_correct <- function(model, mcmc_output, particles, threads = 1L, is_type = "is2", seed = sample(.Machine$integer.max, size = 1)) { - if (!inherits(mcmc_output, "mcmc_output")) stop("Object 'mcmc_output' is not valid output from 'run_mcmc'.") - is_type <- pmatch(match.arg(is_type, paste0("is", 1:3)), paste0("is", 1:3)) + check_missingness(model) + + particles <- check_intmax(particles, "particles") + threads <- check_intmax(threads, "threads") + + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + + if (!inherits(mcmc_output, "mcmc_output")) + stop("Object 'mcmc_output' is not valid output from 'run_mcmc'.") + is_type <- pmatch(match.arg(tolower(is_type), paste0("is", 1:3)), + paste0("is", 1:3)) a <- proc.time() if (inherits(model, "nongaussian")) { model$distribution <- pmatch(model$distribution, - c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), - duplicates.ok = TRUE) - 1 + c("svm", "poisson", "binomial", "negative binomial", "gamma", + "gaussian"), duplicates.ok = TRUE) - 1 out <- postcorrection_nongaussian(model, model_type(model), mcmc_output$output_type, particles, - seed, threads, is_type, mcmc_output$counts, t(mcmc_output$theta), mcmc_output$modes) + seed, threads, is_type, mcmc_output$counts, t(mcmc_output$theta), + mcmc_output$modes) } else { if (inherits(model, "ssm_nlg")) { out <- postcorrection_nonlinear(t(model$y), model$Z, model$H, model$T, @@ -212,7 +279,9 @@ post_correct <- function(model, mcmc_output, particles, threads = 1L, particles, seed, threads, is_type, mcmc_output$counts, t(mcmc_output$theta), mcmc_output$modes) - } else stop("Function 'post_correct' is only available for models of class 'nongaussian' and 'ssm_nlg'.") + } else + stop(paste("Function 'post_correct' is only available for models of", + "class 'nongaussian' and 'ssm_nlg'.", sep = " ")) } mcmc_output$weights <- out$weights mcmc_output$posterior <- mcmc_output$posterior + out$posterior @@ -223,16 +292,17 @@ post_correct <- function(model, mcmc_output, particles, threads = 1L, if (mcmc_output$output_type == 2) { mcmc_output$alphahat <- out$alphahat mcmc_output$Vt <- out$Vt - colnames(mcmc_output$alphahat) <- colnames(mcmc_output$Vt) <- rownames(mcmc_output$Vt) <- - names(model$a1) + colnames(mcmc_output$alphahat) <- colnames(mcmc_output$Vt) <- + rownames(mcmc_output$Vt) <- names(model$a1) mcmc_output$alphahat <- ts(mcmc_output$alphahat, start = start(model$y), frequency = frequency(model$y)) } } mcmc_output$time <- - rbind("approx" = mcmc_output$time, "postcorrection" = proc.time() - a)[,1:3] + rbind("approx" = mcmc_output$time, + "postcorrection" = proc.time() - a)[, 1:3] mcmc_output$mcmc_type <- paste0("is", is_type) mcmc_output$seed <- c(mcmc_output$seed, seed) mcmc_output$call <- c(mcmc_output$call, match.call()) mcmc_output -} \ No newline at end of file +} diff --git a/R/predict.R b/R/predict.R index 595ad1b3..b9b468cc 100644 --- a/R/predict.R +++ b/R/predict.R @@ -1,30 +1,42 @@ #' Predictions for State Space Models #' -#' Draw samples from the posterior predictive distribution for future time points -#' given the posterior draws of hyperparameters \eqn{\theta} and \eqn{alpha_{n+1}}. -#' Function can also be used to draw samples from the posterior predictive distribution -#' \eqn{p(\tilde y_1, \ldots, \tilde y_n | y_1,\ldots, y_n)}. -#' -#' @param object mcmc_output object obtained from -#' \code{\link{run_mcmc}} -#' @param type Return predictions on \code{"mean"} -#' \code{"response"}, or \code{"state"} level. -#' @param model Model for future observations. -#' Should have same structure as the original model which was used in MCMC, -#' in order to plug the posterior samples of the model parameters to the right places. -#' It is also possible to input the original model, which can be useful for example for -#' posterior predictive checks. In this case, set argument \code{future} to \code{FALSE}. -#' @param nsim Number of samples to draw. -#' @param future Default is \code{TRUE}, in which case predictions are future. +#' Draw samples from the posterior predictive distribution for future +#' time points given the posterior draws of hyperparameters \eqn{\theta} and +#' latent state \eqn{alpha_{n+1}} returned by \code{run_mcmc}. +#' Function can also be used to draw samples from the posterior predictive +#' distribution \eqn{p(\tilde y_1, \ldots, \tilde y_n | y_1,\ldots, y_n)}. +#' +#' @seealso \code{fitted} for in-sample predictions. +#' @param object Results object of class \code{mcmc_output} from +#' \code{\link{run_mcmc}}. +#' @param model A \code{bssm_model} object. +#' Should have same structure and class as the original model which was used in +#' \code{run_mcmc}, in order to plug the posterior samples of the model +#' parameters to the right places. +#' It is also possible to input the original model for obtaining predictions +#' for past time points. In this case, set argument +#' \code{future} to \code{FALSE}. +#' @param type Type of predictions. Possible choices are +#' \code{"mean"} \code{"response"}, or \code{"state"} level. +#' @param nsim Positive integer defining number of samples to draw. Should be +#' less than or equal to \code{sum(object$counts)} i.e. the number of samples +#' in the MCMC output. Default is to use all the samples. +#' @param future Default is \code{TRUE}, in which case predictions are for the +#' future, using posterior samples of (theta, alpha_T+1) i.e. the +#' posterior samples of hyperparameters and latest states. #' Otherwise it is assumed that \code{model} corresponds to the original model. -#' @param seed Seed for RNG. +#' @param seed Seed for the C++ RNG (positive integer). Note that this affects +#' only the C++ side, and \code{predict} also uses R side RNG for subsampling, +#' so for replicable results you should call \code{set.seed} before +#' \code{predict}. #' @param ... Ignored. -#' @return Data frame of predicted samples. +#' @return A data.frame consisting of samples from the predictive +#' posterior distribution. #' @method predict mcmc_output -#' @rdname predict +#' @aliases predict predict.mcmc_output #' @export #' @examples -#' require("graphics") +#' library("graphics") #' y <- log10(JohnsonJohnson) #' prior <- uniform(0.01, 0, 1) #' model <- bsm_lg(window(y, end = c(1974, 4)), sd_y = prior, @@ -36,39 +48,39 @@ #' start = tsp(model$y)[2] + 2 * deltat(model$y), #' frequency = frequency(model$y)) #' # use "state" for illustrative purposes, we could use type = "mean" directly -#' pred <- predict(mcmc_results, future_model, type = "state", +#' pred <- predict(mcmc_results, model = future_model, type = "state", #' nsim = 1000) #' -#' require("dplyr") -#' sumr_fit <- as.data.frame(mcmc_results, variable = "states") %>% -#' group_by(time, iter) %>% +#' library("dplyr") +#' sumr_fit <- as.data.frame(mcmc_results, variable = "states") |> +#' group_by(time, iter) |> #' mutate(signal = #' value[variable == "level"] + -#' value[variable == "seasonal_1"]) %>% -#' group_by(time) %>% +#' value[variable == "seasonal_1"]) |> +#' group_by(time) |> #' summarise(mean = mean(signal), #' lwr = quantile(signal, 0.025), #' upr = quantile(signal, 0.975)) #' -#' sumr_pred <- pred %>% -#' group_by(time, sample) %>% +#' sumr_pred <- pred |> +#' group_by(time, sample) |> #' mutate(signal = #' value[variable == "level"] + -#' value[variable == "seasonal_1"]) %>% -#' group_by(time) %>% +#' value[variable == "seasonal_1"]) |> +#' group_by(time) |> #' summarise(mean = mean(signal), #' lwr = quantile(signal, 0.025), #' upr = quantile(signal, 0.975)) #' #' # If we used type = "mean", we could do -#' # sumr_pred <- pred %>% -#' # group_by(time) %>% +#' # sumr_pred <- pred |> +#' # group_by(time) |> #' # summarise(mean = mean(value), #' # lwr = quantile(value, 0.025), #' # upr = quantile(value, 0.975)) #' -#' require("ggplot2") -#' rbind(sumr_fit, sumr_pred) %>% +#' library("ggplot2") +#' rbind(sumr_fit, sumr_pred) |> #' ggplot(aes(x = time, y = mean)) + #' geom_ribbon(aes(ymin = lwr, ymax = upr), #' fill = "#92f0a8", alpha = 0.25) + @@ -79,27 +91,28 @@ #' time = time(JohnsonJohnson))) #' #' # Posterior predictions for past observations: -#' yrep <- predict(mcmc_results, model, type = "response", +#' yrep <- predict(mcmc_results, model = model, type = "response", #' future = FALSE, nsim = 1000) -#' meanrep <- predict(mcmc_results, model, type = "mean", +#' meanrep <- predict(mcmc_results, model = model, type = "mean", #' future = FALSE, nsim = 1000) #' -#' sumr_yrep <- yrep %>% -#' group_by(time) %>% +#' sumr_yrep <- yrep |> +#' group_by(time) |> #' summarise(earnings = mean(value), #' lwr = quantile(value, 0.025), -#' upr = quantile(value, 0.975)) %>% +#' upr = quantile(value, 0.975)) |> #' mutate(interval = "Observations") #' -#' sumr_meanrep <- meanrep %>% -#' group_by(time) %>% +#' sumr_meanrep <- meanrep |> +#' group_by(time) |> #' summarise(earnings = mean(value), #' lwr = quantile(value, 0.025), -#' upr = quantile(value, 0.975)) %>% +#' upr = quantile(value, 0.975)) |> #' mutate(interval = "Mean") #' -#' rbind(sumr_meanrep, sumr_yrep) %>% -#' mutate(interval = factor(interval, levels = c("Observations", "Mean"))) %>% +#' rbind(sumr_meanrep, sumr_yrep) |> +#' mutate(interval = +#' factor(interval, levels = c("Observations", "Mean"))) |> #' ggplot(aes(x = time, y = earnings)) + #' geom_ribbon(aes(ymin = lwr, ymax = upr, fill = interval), #' alpha = 0.75) + @@ -109,68 +122,111 @@ #' time = time(model$y))) #' #' -predict.mcmc_output <- function(object, model, type = "response", nsim, future = TRUE, - seed = sample(.Machine$integer.max, size = 1), ...) { +predict.mcmc_output <- function(object, model, nsim, type = "response", + future = TRUE, seed = sample(.Machine$integer.max, size = 1), ...) { - type <- match.arg(type, c("response", "mean", "state")) - - if (object$output_type != 1) stop("MCMC output must contain posterior samples of the states.") + check_missingness(model) + if (!inherits(model, "bssm_model")) { + stop("Argument 'model' should be an object of class 'bssm_model'.") + } + if (object$output_type != 1) + stop("MCMC output must contain posterior samples of the states.") - if(!identical(attr(object, "model_type"), class(model)[1])) { + if (!identical(attr(object, "model_type"), class(model)[1])) { stop("Model class does not correspond to the MCMC output. ") } - if(!identical(ncol(object$theta), length(model$theta))) { - stop("Number of unknown parameters 'theta' does not correspond to the MCMC output. ") + if (!identical(ncol(object$theta), length(model$theta))) { + stop(paste("Number of unknown parameters 'theta' does not correspond to", + "the MCMC output. ", sep = " ")) } + if (missing(nsim)) { + nsim <- sum(object$counts) + } else { + nsim <- check_intmax(nsim, "nsim", positive = TRUE, max = Inf) + if (nsim > sum(object$count)) + stop(paste0("The number of samples should be smaller than or equal to ", + "the number of posterior samples ", sum(object$counts), ".")) + } + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) - if(future) { - - if (attr(object, "model_type") %in% c("bsm_lg", "bsm_ng")) { - object$theta[,1:(ncol(object$theta) - length(model$beta))] <- - log(object$theta[,1:(ncol(object$theta) - length(model$beta))]) + if (!test_flag(future)) stop("Argument 'future' should be TRUE or FALSE. ") + + type <- match.arg(tolower(type), c("response", "mean", "state")) + + if (attr(object, "model_type") %in% c("bsm_lg", "bsm_ng")) { + object$theta[, 1:(ncol(object$theta) - length(model$beta))] <- + log(object$theta[, 1:(ncol(object$theta) - length(model$beta))]) + } else { + if (attr(object, "model_type") == "ar1_lg") { + object$theta[, c("sigma", "sd_y")] <- + log(object$theta[, c("sigma", "sd_y")]) + } else { + if (attr(object, "model_type") == "ar1_ng") { + disp <- ifelse( + object$distribution %in% c("negative binomial", "gamma"), + "phi", NULL) + object$theta[, c("sigma", disp)] <- + log(object$theta[, c("sigma", disp)]) + } } - w <- object$counts * (if(object$mcmc_type %in% paste0("is", 1:3)) object$weights else 1) - idx <- sample(1:nrow(object$theta), size = nsim, prob = w, replace = TRUE) - theta <- t(object$theta[idx, ]) - alpha <- matrix(object$alpha[nrow(object$alpha),,idx], nrow = ncol(object$alpha)) + } + + idx <- sample(seq_len(sum(object$counts)), size = nsim) + if (object$mcmc_type %in% paste0("is", 1:3)) { + weight <- rep(object$weights, times = object$counts)[idx] + } else { + weight <- rep(1, nsim) + } + theta <- + t(apply(object$theta, 2, rep, times = object$counts)[idx, , drop = FALSE]) + + if (future) { + + states <- t(apply(object$alpha[nrow(object$alpha), , , drop = FALSE], + 2, rep, object$counts)[idx, , drop = FALSE]) switch(attr(object, "model_type"), - ssm_mlg = , - ssm_ulg = , - bsm_lg = , + ssm_mlg =, + ssm_ulg =, + bsm_lg =, ar1_lg = { if (!identical(length(model$a1), ncol(object$alpha))) { - stop("Model does not correspond to the MCMC output: Wrong number of states. ") + stop(paste("Model does not correspond to the MCMC output:", + "Wrong number of states. ", sep = " ")) } - pred <- gaussian_predict(model, theta, alpha, + pred <- gaussian_predict(model, theta, states, pmatch(type, c("response", "mean", "state")), seed, pmatch(attr(object, "model_type"), - c("ssm_mng", "ssm_ulg", "bsm_lg", "ar1_lg")) - 1L) + c("ssm_mlg", "ssm_ulg", "bsm_lg", "ar1_lg")) - 1L) }, - ssm_mng = , - ssm_ung = , - bsm_ng = , - svm = , + ssm_mng =, + ssm_ung =, + bsm_ng =, + svm =, ar1_ng = { if (!identical(length(model$a1), ncol(object$alpha))) { - stop("Model does not correspond to the MCMC output: Wrong number of states. ") + stop(paste("Model does not correspond to the MCMC output:", + "Wrong number of states. ", sep = " ")) } model$distribution <- pmatch(model$distribution, - c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), + c("svm", "poisson", "binomial", "negative binomial", "gamma", + "gaussian"), duplicates.ok = TRUE) - 1 - pred <- nongaussian_predict(model, theta, alpha, + pred <- nongaussian_predict(model, theta, states, pmatch(type, c("response", "mean", "state")), seed, pmatch(attr(object, "model_type"), c("ssm_mng", "ssm_ung", "bsm_ng", "svm", "ar1_ng")) - 1L) - if(anyNA(pred)) warning("NA or NaN values in predictions, possible under/overflow?") + if (anyNA(pred)) + warning("NA or NaN values in predictions, possible under/overflow?") }, ssm_nlg = { if (!identical(model$n_states, ncol(object$alpha))) { - stop("Model does not correspond to the MCMC output: Wrong number of states. ") + stop(paste("Model does not correspond to the MCMC output:", + "Wrong number of states. ", sep = " ")) } pred <- nonlinear_predict(t(model$y), model$Z, model$H, model$T, model$R, model$Z_gn, @@ -178,97 +234,105 @@ predict.mcmc_output <- function(object, model, type = "response", nsim, future = model$log_prior_pdf, model$known_params, model$known_tv_params, as.integer(model$time_varying), model$n_states, model$n_etas, - theta, alpha, pmatch(type, c("response", "mean", "state")), seed) + theta, states, pmatch(type, c("response", "mean", "state")), seed) } , stop("Not yet implemented for ssm_sde. ")) - if(type == "state") { - if(attr(object, "model_type") == "ssm_nlg") { + + if (type == "state") { + if (attr(object, "model_type") == "ssm_nlg") { variables <- model$state_names } else { variables <- names(model$a1) } } else { variables <- colnames(model$y) - if(is.null(variables)) variables <- "Series 1" + if (is.null(variables)) + variables <- paste("Series", 1:max(1, ncol(model$y))) } d <- data.frame(value = as.numeric(pred), variable = variables, time = rep(time(model$y), each = nrow(pred)), + weight = rep(weight, each = nrow(pred) * ncol(pred)), sample = rep(1:nsim, each = nrow(pred) * ncol(pred))) } else { - if(!identical(nrow(object$alpha) - 1L, length(model$y))) { - stop("Number of observations of the model and MCMC output do not match. ") + if (inherits(model, c("ssm_mng", "ssm_mlg", "ssm_nlg"))) { + if (!identical(nrow(object$alpha) - 1L, nrow(model$y))) { + stop(paste0("Number of observations in the model and MCMC output do ", + "not match.")) + } + } else { + if (!identical(nrow(object$alpha) - 1L, length(model$y))) { + stop(paste0("Number of observations in the model and MCMC output do ", + "not match.")) + } } - w <- object$counts * (if(object$mcmc_type %in% paste0("is", 1:3)) object$weights else 1) - idx <- sample(1:nrow(object$theta), size = nsim, prob = w, replace = TRUE) n <- nrow(object$alpha) - 1L - m <- ncol(object$alpha) + states <- + apply(object$alpha, 1:2, rep, object$counts)[idx, 1:n, , drop = FALSE] - states <- object$alpha[1:n, , idx, drop = FALSE] - - if(type == "state") { - if(attr(object, "model_type") == "ssm_nlg") { + if (type == "state") { + if (attr(object, "model_type") == "ssm_nlg") { variables <- model$state_names } else { variables <- names(model$a1) } d <- data.frame(value = as.numeric(states), - variable = rep(variables, each = n), - time = rep(time(model$y), times = m), - sample = rep(1:nsim, each = n * m)) + variable = rep(variables, each = nrow(states) * n), + time = rep(time(model$y), times = nrow(states)), + sample = 1:nsim, weight = weight) } else { variables <- colnames(model$y) - if(is.null(variables)) variables <- "Series 1" - - if (attr(object, "model_type") %in% c("bsm_lg", "bsm_ng")) { - object$theta[,1:(ncol(object$theta) - length(model$beta))] <- - log(object$theta[,1:(ncol(object$theta) - length(model$beta))]) - } - theta <- t(object$theta[idx, ]) - states <- aperm(states, c(2, 1, 3)) + if (is.null(variables)) + variables <- paste("Series", 1:max(1, ncol(model$y))) + + states <- aperm(states, 3:1) - switch(attr(object, "model_type"), - ssm_mlg = , - ssm_ulg = , - bsm_lg = , + ssm_mlg =, + ssm_ulg =, + bsm_lg =, ar1_lg = { - if (!identical(length(model$a1), m)) { - stop("Model does not correspond to the MCMC output: Wrong number of states. ") + if (!identical(length(model$a1), nrow(states))) { + stop(paste("Model does not correspond to the MCMC output:", + "Wrong number of states. ", sep = " ")) } pred <- gaussian_predict_past(model, theta, states, pmatch(type, c("response", "mean", "state")), seed, pmatch(attr(object, "model_type"), - c("ssm_mng", "ssm_ulg", "bsm_lg", "ar1_lg")) - 1L) + c("ssm_mlg", "ssm_ulg", "bsm_lg", "ar1_lg")) - 1L) }, - ssm_mng = , - ssm_ung = , - bsm_ng = , - svm = , + ssm_mng =, + ssm_ung =, + bsm_ng =, + svm =, ar1_ng = { - if (!identical(length(model$a1), m)) { - stop("Model does not correspond to the MCMC output: Wrong number of states. ") + if (!identical(length(model$a1), nrow(states))) { + stop(paste("Model does not correspond to the MCMC output:", + "Wrong number of states. ", sep = " ")) } model$distribution <- pmatch(model$distribution, - c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), + c("svm", "poisson", "binomial", "negative binomial", "gamma", + "gaussian"), duplicates.ok = TRUE) - 1 pred <- nongaussian_predict_past(model, theta, states, pmatch(type, c("response", "mean", "state")), seed, pmatch(attr(object, "model_type"), c("ssm_mng", "ssm_ung", "bsm_ng", "svm", "ar1_ng")) - 1L) - if(anyNA(pred)) warning("NA or NaN values in predictions, possible under/overflow?") + if (anyNA(pred)) + warning("NA or NaN values in predictions, possible under/overflow?") }, ssm_nlg = { - if (!identical(model$n_states, m)) { - stop("Model does not correspond to the MCMC output: Wrong number of states. ") + if (!identical(model$n_states, nrow(states))) { + stop(paste("Model does not correspond to the MCMC output:", + "Wrong number of states. ", sep = " ")) } pred <- nonlinear_predict_past(t(model$y), model$Z, model$H, model$T, model$R, model$Z_gn, @@ -280,11 +344,12 @@ predict.mcmc_output <- function(object, model, type = "response", nsim, future = } , stop("Not yet implemented for ssm_sde. ")) - + d <- data.frame(value = as.numeric(pred), variable = variables, time = rep(time(model$y), each = nrow(pred)), - sample = rep(1:nsim, each = nrow(pred) * ncol(pred))) + sample = rep(1:nsim, each = nrow(pred) * ncol(pred)), + weight = rep(weight, each = nrow(pred) * ncol(pred))) } } d diff --git a/R/print_mcmc.R b/R/print_mcmc.R index 0fa5f8c9..28136a0a 100644 --- a/R/print_mcmc.R +++ b/R/print_mcmc.R @@ -1,295 +1,48 @@ -iact <- function(x) { - n <- length(x) - x_ <- (x - mean(x)) / sd(x) - # Sokal: Monte Carlo Methods in Statistical Mechanics: Foundations and New Algorithms - C <- max(5.0, log10(n)) - tau <- 1 - for(k in 1:(n-1)) { - tau <- tau + 2.0 * (x_[1:(n-k)] %*% x_[(1+k):n]) / (n - k) - if(k > C * tau) break - } - max(0.0, tau) -} -#' Asymptotic Variance of IS-type Estimators -#' -#' Estimates the asymptotic variance based on Corollary 1 -#' of Vihola et al. (2020) from weighted samples from IS-MCMC. -#' -#' @param x Vector of samples. -#' @param w Vector of weights. -#' @export -asymptotic_var <- function(x, w) { - estimate_c <- mean(w) - estimate_mean <- weighted_mean(x, w) - z <- w * (x - estimate_mean) - iact(z) * var(z) / length(z) / estimate_c^2 -} - #' Print Results from MCMC Run #' -#' Prints some basic summaries from the MCMC run by \code{\link{run_mcmc}}. +#' Prints some basic summaries from the MCMC run by \code{\link{run_mcmc}}. #' #' @method print mcmc_output #' @importFrom diagis weighted_mean weighted_var weighted_se ess -#' @importFrom coda mcmc spectrum0.ar #' @importFrom stats var -#' @param x Output from \code{\link{run_mcmc}}. +#' @param x Object of class \code{mcmc_output} from \code{\link{run_mcmc}}. #' @param ... Ignored. +#' @srrstats {BS5.3, BS5.5, BS6.0} #' @export +#' @examples +#' data("negbin_model") +#' print(negbin_model) print.mcmc_output <- function(x, ...) { - if (x$mcmc_type %in% paste0("is", 1:3)) { - theta <- mcmc(x$theta) - if(x$output_type == 1) - alpha <- mcmc(matrix(x$alpha[nrow(x$alpha),,], ncol = ncol(x$alpha), byrow = TRUE, - dimnames = list(NULL, colnames(x$alpha)))) - w <- x$counts * x$weights - } else { - theta <- expand_sample(x, "theta") - if(x$output_type == 1) - alpha <- expand_sample(x, "state", times = nrow(x$alpha), by_states = FALSE)[[1]] - } - cat("\nCall:\n", paste(deparse(x$call), sep = "\n", collapse = "\n"), "\n", sep = "") cat("\n", "Iterations = ", x$burnin + 1, ":", x$iter, "\n", sep = "") - cat("Thinning interval = ",x$thin, "\n", sep = "") + cat("Thinning interval = ", x$thin, "\n", sep = "") cat("Length of the final jump chain = ", length(x$counts), "\n", sep = "") - cat("\nAcceptance rate after the burn-in period: ", paste(round(x$acceptance_rate,3),"\n", sep = "")) + cat("\nAcceptance rate after the burn-in period: ", + paste(round(x$acceptance_rate, 3), "\n", sep = "")) cat("\nSummary for theta:\n\n") - if (x$mcmc_type %in% paste0("is", 1:3)) { - mean_theta <- weighted_mean(theta, w) - sd_theta <- sqrt(diag(weighted_var(theta, w, method = "moment"))) - se_theta_is <- weighted_se(theta, w) - se_theta <- sqrt(apply(theta, 2, function(x) asymptotic_var(x, w))) - - stats <- matrix(c(mean_theta, sd_theta, se_theta, se_theta_is), ncol = 4, - dimnames = list(colnames(x$theta), c("Mean", "SD", "SE", "SE-IS"))) - } else { - mean_theta <- colMeans(theta) - sd_theta <- apply(theta, 2, sd) - se_theta <- sqrt(spectrum0.ar(theta)$spec/nrow(theta)) - stats <- matrix(c(mean_theta, sd_theta, se_theta), ncol = 3, - dimnames = list(colnames(x$theta), c("Mean", "SD", "SE"))) - } - - print(stats) - - cat("\nEffective sample sizes for theta:\n\n") - esss <- matrix((sd_theta / se_theta)^2, ncol = 1, - dimnames = list(colnames(x$theta), c("ESS"))) - print(esss) - if(x$output_type != 3) { - + stats <- as.data.frame(summary(x, variable = "theta", return_se = TRUE)) + print(stats, row.names = FALSE) + if (x$output_type != 3) { n <- nrow(x$alpha) cat(paste0("\nSummary for alpha_", n), ":\n\n", sep = "") if (is.null(x$alphahat)) { - if (x$mcmc_type %in% paste0("is", 1:3)) { - mean_alpha <- weighted_mean(alpha, w) - sd_alpha <- sqrt(diag(weighted_var(alpha, w, method = "moment"))) - se_alpha_is <- weighted_se(alpha, w) - se_alpha <- sqrt(apply(alpha, 2, function(x) asymptotic_var(x, w))) - stats <- matrix(c(mean_alpha, sd_alpha, se_alpha,se_alpha_is), ncol = 4, - dimnames = list(colnames(x$alpha), c("Mean", "SD", "SE", "SE-IS"))) - } else { - mean_alpha <- colMeans(alpha) - sd_alpha <- apply(alpha, 2, sd) - se_alpha <- sqrt(spectrum0.ar(alpha)$spec / nrow(alpha)) - stats <- matrix(c(mean_alpha, sd_alpha, se_alpha), ncol = 3, - dimnames = list(colnames(x$alpha), c("Mean", "SD", "SE"))) - } - print(stats) - - - cat(paste0("\nEffective sample sizes for alpha_", n), ":\n\n", sep = "") - esss <- matrix((sd_alpha / se_alpha)^2, ncol = 1, - dimnames = list(colnames(x$alpha), c("ESS"))) - - print(esss) - + stats <- as.data.frame(summary(x, variable = "states", times = n, + return_se = TRUE)) + print(stats, row.names = FALSE) } else { if (ncol(x$alphahat) == 1) { - print(cbind("Mean" = x$alphahat[n, ], "SD" = sqrt(x$Vt[,,n]))) + print(cbind("Mean" = x$alphahat[n, ], "SD" = sqrt(x$Vt[, , n]))) } else { - print(cbind("Mean" = x$alphahat[n, ], "SD" = sqrt(diag(x$Vt[,,n])))) + print(cbind("Mean" = x$alphahat[n, ], "SD" = sqrt(diag(x$Vt[, , n])))) } } } else cat("\nNo posterior samples for states available.\n") cat("\nRun time:\n") print(x$time) -} - -#' Summary of MCMC object -#' -#' This functions returns a list containing mean, standard deviations, standard errors, and -#' effective sample size estimates for parameters and states. -#' -#' For IS-MCMC two types of standard errors are reported. -#' SE-IS can be regarded as the square root of independent IS variance, -#' whereas SE corresponds to the square root of total asymptotic variance ( -#' see Remark 3 of Vihola et al. (2020)). -#' -#' @param object Output from \code{run_mcmc} -#' @param return_se if \code{FALSE} (default), computation of standard -#' errors and effective sample sizes is omitted. -#' @param variable Are the summary statistics computed for either \code{"theta"} (default), -#' \code{"states"}, or \code{"both"}? -#' @param only_theta Deprecated. If \code{TRUE}, summaries are computed only for hyperparameters theta. -#' @param ... Ignored. -#' @references -#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -#' Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 -#' @export -summary.mcmc_output <- function(object, return_se = FALSE, variable = "theta", - only_theta = FALSE, ...) { - - if (only_theta) { - parameters <- "theta" - warning("Argument 'only_theta' is deprecated. Use argument 'variable' instead. ") - } - variable <- match.arg(variable, c("theta", "states", "both")) - - if(variable %in% c("theta", "both")) { - if (object$mcmc_type %in% paste0("is", 1:3)) { - theta <- mcmc(object$theta) - w <- object$counts * object$weights - mean_theta <- weighted_mean(theta, w) - sd_theta <- sqrt(diag(weighted_var(theta, w, method = "moment"))) - - if(return_se) { - mean_theta <- weighted_mean(theta, w) - sd_theta <- sqrt(diag(weighted_var(theta, w, method = "moment"))) - se_theta_is <- weighted_se(theta, w) - se_theta <- sqrt(apply(theta, 2, function(x) asymptotic_var(x, w))) - ess_theta <- (sd_theta / se_theta)^2 - ess_w <- apply(object$theta, 2, function(x) ess(w, identity, x)) - summary_theta <- matrix(c(mean_theta, sd_theta, se_theta, ess_theta, se_theta_is, ess_w), ncol = 6, - dimnames = list(colnames(object$theta), - c("Mean", "SD", "SE", "ESS", "SE-IS", "ESS-IS"))) - } else { - summary_theta <- matrix(c(mean_theta, sd_theta), ncol = 2, - dimnames = list(colnames(object$theta), c("Mean", "SD"))) - } - } else { - theta <- expand_sample(object, "theta") - mean_theta <- colMeans(theta) - sd_theta <- apply(theta, 2, sd) - - if(return_se) { - mean_theta <- colMeans(theta) - sd_theta <- apply(theta, 2, sd) - se_theta <- sqrt(spectrum0.ar(theta)$spec/nrow(theta)) - ess_theta <- (sd_theta / se_theta)^2 - summary_theta <- matrix(c(mean_theta, sd_theta, se_theta, ess_theta), ncol = 4, - dimnames = list(colnames(object$theta), c("Mean", "SD", "SE", "ESS"))) - } else { - summary_theta <- matrix(c(mean_theta, sd_theta), ncol = 2, - dimnames = list(colnames(object$theta), c("Mean", "SD"))) - } - } - } - - if (variable %in% c("states", "both")) { - if (object$output_type != 1) stop("Cannot return summary of states as the MCMC type was not 'full'. ") - - m <- ncol(object$alpha) - - if (object$mcmc_type %in% paste0("is", 1:3)) { - w <- object$counts * object$weights - mean_alpha <- ts(weighted_mean(object$alpha, w), start = attr(object, "ts")$start, - frequency = attr(object, "ts")$frequency, names = colnames(object$alpha)) - sd_alpha <- weighted_var(object$alpha, w, method = "moment") - sd_alpha <- if(m > 1) sqrt(t(apply(sd_alpha, 3, diag))) else matrix(sqrt(sd_alpha), ncol = 1) - - - if(return_se) { - se_alpha_is <- apply(object$alpha, 2, function(x) weighted_se(t(x), w)) - - se_alpha <- apply(object$alpha, 2, function(z) sqrt(apply(z, 1, function(x) asymptotic_var(x, w)))) - alpha_ess <- (sd_alpha / se_alpha)^2 - ess_w <- apply(object$alpha, 2, function(z) apply(z, 1, function(x) ess(w, identity, x))) - summary_alpha <- list( - "Mean" = mean_alpha, "SD" = sd_alpha, - "SE" = se_alpha, "ESS" = alpha_ess, - "SE-IS" = se_alpha_is, "ESS-IS" = ess_w) - } else { - summary_alpha <- list("Mean" = mean_alpha, "SD" = sd_alpha) - } - - } else { - alpha <- expand_sample(object, "states") - mean_alpha <- ts(sapply(alpha, colMeans), - start = attr(object, "ts")$start, - frequency = attr(object, "ts")$frequency, names = colnames(object$alpha)) - sd_alpha <- sapply(alpha, function(x) apply(x, 2, sd)) - - if(return_se) { - - se_alpha <- sapply(alpha, function(x) - apply(x, 2, function(z) - sqrt(spectrum0.ar(z)$spec / length(z)))) - ess_alpha <- (sd_alpha / se_alpha)^2 - summary_alpha <- list( - "Mean" = mean_alpha, "SD" = sd_alpha, - "SE" = se_alpha, "ESS" = ess_alpha) - } else { - summary_alpha <- list("Mean" = mean_alpha, "SD" = sd_alpha) - } - } - } - switch(variable, - "both" = return(list(theta = summary_theta, states = summary_alpha)), - "theta" = return(summary_theta), - "states" = return(summary_alpha) - ) -} - -#' Expand the Jump Chain representation -#' -#' The MCMC algorithms of \code{bssm} use a jump chain representation where we -#' store the accepted values and the number of times we stayed in the current value. -#' Although this saves bit memory and is especially convenient for IS-corrected -#' MCMC, sometimes we want to have the usual sample paths. Function \code{expand_sample} -#' returns the expanded sample based on the counts. Note that for IS-corrected output the expanded -#' sample corresponds to the approximate posterior. -#' -#' @param x Output from \code{\link{run_mcmc}}. -#' @param variable Expand parameters \code{"theta"} or states \code{"states"}. -#' @param times Vector of indices. In case of states, what time points to expand? Default is all. -#' @param states Vector of indices. In case of states, what states to expand? Default is all. -#' @param by_states If \code{TRUE} (default), return list by states. Otherwise by time. -#' @export -expand_sample <- function(x, variable = "theta", times, states, by_states = TRUE) { - - variable <- match.arg(variable, c("theta", "states")) - if (x$mcmc_type %in% paste0("is", 1:3)) - warning("Input is based on a IS-weighted MCMC, the results correspond to the approximate posteriors.") - if(variable == "theta") { - out <- apply(x$theta, 2, rep, times = x$counts) - } else { - if (x$output_type == 1) { - if(missing(times)) times <- 1:nrow(x$alpha) - if(missing(states)) states <- 1:ncol(x$alpha) - - if(by_states) { - out <- lapply(states, function(i) { - z <- apply(x$alpha[times, i, , drop = FALSE], 1, rep, x$counts) - colnames(z) <- times - z - }) - names(out) <- colnames(x$alpha)[states] - } else { - out <- lapply(times, function(i) { - z <- apply(x$alpha[i, states, , drop = FALSE], 2, rep, x$counts) - colnames(z) <- colnames(x$alpha)[states] - z - }) - names(out) <- times - } - } else stop("MCMC output does not contain posterior samples of states.") - } - mcmc(out, start = x$burnin + 1, thin = x$thin) + invisible(x) } diff --git a/R/priors.R b/R/priors.R index 6deda193..56b0c87d 100644 --- a/R/priors.R +++ b/R/priors.R @@ -1,162 +1,272 @@ -## will add more choices later... -## add recycling of parameters later - #' Prior objects for bssm models #' -#' These simple objects of class \code{bssm_prior} are used to construct a prior distributions for the -#' MCMC runs of \code{bssm} package. Currently supported priors are uniform (\code{uniform()}), -#' half-normal (\code{halfnormal()}), normal (\code{normal()}), gamma (\code{gamma}), and -#' truncated normal distribution (\code{tnormal()}).All parameters are vectorized so -#' for regression coefficient vector beta you can define prior for example -#' as \code{normal(0, 0, c(10, 20))}. -#' -#' -#' @rdname priors -#' @param init Initial value for the parameter, used in initializing the model components and as a starting value -#' in MCMC. +#' These simple objects of class \code{bssm_prior} are used to construct a +#' prior distributions for the hyperparameters theta for some of the model +#' objects of \code{bssm} package. Note that these priors do not include the +#' constant terms as they do not affect the sampling. +#' +#' Currently supported priors are +#' +#' * uniform prior (\code{uniform()}) with a probability density function (pdf) +#' defined as \eqn{\frac{1}{max - min}} for \eqn{min < theta < max}. +#' * normal (\code{normal()}), a normal distribution parameterized via mean and +#' standard deviation, i.e. N(mean, sd^2). +#' * truncated normal distribution (\code{tnormal()}), a normal distribution +#' with known truncation points (from below and/or above). Ignoring the +#' scaling factors, this corresponds to the pdf of N(mean, sd^2) when +#' \eqn{min < theta < max} and zero otherwise. +#' * half-normal (\code{halfnormal()}) with a pdf matching the pdf of the +#' truncated normal distribution with min=0 and max=inf. +#' * gamma (\code{gamma}), a gamma distribution with shape and rate +#' parameterization. +#' +#' All parameters are vectorized so for regression coefficient vector beta you +#' can define prior for example as \code{normal(0, 0, c(10, 20))}. +#' +#' For the general exponential models, i.e. models built with the `ssm_ulg`, +#' `ssm_ung`, `ssm_mlg`, and `ssm_mng`, you can define arbitrary priors by +#' defining the `prior_fn` function, which takes the one argument, `theta`, +#' corresponding to the hyperparameter vector of the model, +#' and returns a log-density of the (joint) prior (see the R Journal paper and +#' e.g. `ssm_ulg` for examples). Similarly, the priors for the non-linear +#' models (`ssm_nlg`) and SDE models (`ssm_sde`) are constructed +#' via C++ snippets (see the vignettes for details). +#' +#' The longer name versions of the prior functions with \code{_prior} ending +#' are identical with shorter versions and they are available only to +#' avoid clash with R's primitive function \code{gamma} (other long prior names +#' are just for consistent naming). +#' +#' @rdname bssm_prior +#' @aliases bssm_prior bssm_prior_list +#' @param init Initial value for the parameter, used in initializing the model +#' components and as a starting values in MCMC. #' @param min Lower bound of the uniform and truncated normal prior. #' @param max Upper bound of the uniform and truncated normal prior. -#' @param sd Standard deviation of the (underlying i.e. non-truncated) Normal distribution. +#' @param sd Positive value defining the standard deviation of the +#' (underlying i.e. non-truncated) Normal distribution. #' @param mean Mean of the Normal prior. -#' @param shape Shape parameter of the Gamma prior. -#' @param rate Rate parameter of the Gamma prior. -#' @return object of class \code{bssm_prior}. +#' @param shape Positive shape parameter of the Gamma prior. +#' @param rate Positive rate parameter of the Gamma prior. +#' @return object of class \code{bssm_prior} or \code{bssm_prior_list} in case +#' of multiple priors (i.e. multiple regression coefficients). #' @export +#' @srrstats {BS1.2c, BS2.2, BS2.3, BS2.4, BS2.6, BS2.7} Explains prior +#' definitions and initial values. +#' @srrstats {BS2.5} Checks are in place for the distributional parameters of +#' priors and their initial values. #' @examples +#' #' # create uniform prior on [-1, 1] for one parameter with initial value 0.2: -#' uniform(0.2, -1, 1) +#' uniform(init = 0.2, min = -1.0, max = 1.0) #' # two normal priors at once i.e. for coefficients beta: -#' normal(init = c(0.1, 2), mean = 0, sd = c(1, 2)) -uniform <- function(init, min, max){ - if(any(!is.numeric(init), !is.numeric(min), !is.numeric(max))) { +#' normal(init = c(0.1, 2.5), mean = 0.1, sd = c(1.5, 2.8)) +#' # Gamma prior (not run because autotest tests complain) +#' # gamma(init = 0.1, shape = 2.5, rate = 1.1) +#' # Same as +#' gamma_prior(init = 0.1, shape = 2.5, rate = 1.1) +#' # Half-normal +#' halfnormal(init = 0.01, sd = 0.1) +#' # Truncated normal +#' tnormal(init = 5.2, mean = 5.0, sd = 3.0, min = 0.5, max = 9.5) +#' +#' +#' # Further examples for diagnostic purposes: +#' uniform(c(0, 0.2), c(-1.0, 0.001), c(1.0, 1.2)) +#' normal(c(0, 0.2), c(-1.0, 0.001), c(1.0, 1.2)) +#' tnormal(c(2, 2.2), c(-1.0, 0.001), c(1.0, 1.2), c(1.2, 2), 3.3) +#' halfnormal(c(0, 0.2), c(1.0, 1.2)) +#' # not run because autotest bug +#' # gamma(c(0.1, 0.2), c(1.2, 2), c(3.3, 3.3)) +#' +#' # longer versions: +#' uniform_prior(init = c(0, 0.2), min = c(-1.0, 0.001), max = c(1.0, 1.2)) +#' normal_prior(init = c(0, 0.2), mean = c(-1.0, 0.001), sd = c(1.0, 1.2)) +#' tnormal_prior(init = c(2, 2.2), mean = c(-1.0, 0.001), sd = c(1.0, 1.2), +#' min = c(1.2, 2), max = 3.3) +#' halfnormal_prior(init = c(0, 0.2), sd = c(1.0, 1.2)) +#' gamma_prior(init = c(0.1, 0.2), shape = c(1.2, 2), rate = c(3.3, 3.3)) +#' +uniform_prior <- function(init, min, max) { + if (any(!is.numeric(init), !is.numeric(min), !is.numeric(max))) { stop("Parameters for priors must be numeric.") } - if (any(min > max)){ - stop("Lower bound of uniform distribution must be smaller than upper bound.") + if (any(min > max)) { + stop(paste("Lower bound of uniform distribution must be smaller than", + "upper bound.", sep = " ")) } - if(any(init < min) || any(init > max)) { - stop("Initial value for parameter with uniform prior is not in the support of the prior.") + if (any(init < min) || any(init > max)) { + stop(paste("Initial value for parameter with uniform prior is not", + "in the support of the prior.", sep = " ")) } n <- max(length(init), length(min), length(max)) - - if(n > 1) { - structure(lapply(1:n, function(i) structure(list(prior_distribution = "uniform", init = safe_pick(init, i), - min = safe_pick(min, i), max = safe_pick(max, i)), class = "bssm_prior_list")), + + if (n > 1) { + structure(lapply(1:n, function(i) + structure(list(prior_distribution = "uniform", init = safe_pick(init, i), + min = safe_pick(min, i), max = safe_pick(max, i)), + class = "bssm_prior_list")), class = "bssm_prior_list") } else { - structure(list(prior_distribution = "uniform", init = init, min = min, max = max), class = "bssm_prior") + structure(list(prior_distribution = "uniform", init = init, + min = min, max = max), class = "bssm_prior") } } +#' @rdname bssm_prior +#' @export +uniform <- uniform_prior -#' @rdname priors +#' @rdname bssm_prior #' @export -halfnormal <- function(init, sd){ - - if(any(!is.numeric(init), !is.numeric(sd))) { +halfnormal_prior <- function(init, sd) { + + if (any(!is.numeric(init), !is.numeric(sd))) { stop("Parameters for priors must be numeric.") } if (any(sd < 0)) { - stop("Standard deviation parameter for half-Normal distribution must be positive.") + stop(paste("Standard deviation parameter for half-Normal distribution must", + "be positive.", sep = " ")) } if (any(init < 0)) { - stop("Initial value for parameter with half-Normal prior must be non-negative.") + stop(paste("Initial value for parameter with half-Normal prior must be", + "non-negative.", sep = " ")) } n <- max(length(init), length(sd)) - + if (n > 1) { - structure(lapply(1:n, function(i) structure(list(prior_distribution = "halfnormal", init = safe_pick(init, i), + structure(lapply(1:n, function(i) + structure(list(prior_distribution = "halfnormal", + init = safe_pick(init, i), sd = safe_pick(sd, i)), class = "bssm_prior")), class = "bssm_prior_list") } else { - structure(list(prior_distribution = "halfnormal", init = init, sd = sd), class = "bssm_prior") + structure(list(prior_distribution = "halfnormal", init = init, sd = sd), + class = "bssm_prior") } } +#' @rdname bssm_prior +#' @export +halfnormal <- halfnormal_prior - -#' @rdname priors +#' @rdname bssm_prior #' @export -normal <- function(init, mean, sd){ - - if(any(!is.numeric(init), !is.numeric(mean), !is.numeric(sd))) { +normal_prior <- function(init, mean, sd) { + + if (any(!is.numeric(init), !is.numeric(mean), !is.numeric(sd))) { stop("Parameters for priors must be numeric.") } if (any(sd < 0)) { - stop("Standard deviation parameter for Normal distribution must be positive.") + stop(paste("Standard deviation parameter for Normal distribution must", + "be positive.", sep = " ")) } - + n <- max(length(init), length(mean), length(sd)) if (n > 1) { - structure(lapply(1:n, function(i) structure(list(prior_distribution = "normal", - init = safe_pick(init, i), mean = safe_pick(mean, i), sd = safe_pick(sd, i)), + structure(lapply(1:n, function(i) + structure(list(prior_distribution = "normal", + init = safe_pick(init, i), mean = safe_pick(mean, i), + sd = safe_pick(sd, i)), class = "bssm_prior")), class = "bssm_prior_list") } else { - structure(list(prior_distribution = "normal", init = init, mean = mean, sd = sd), - class = "bssm_prior") + structure(list(prior_distribution = "normal", init = init, mean = mean, + sd = sd), class = "bssm_prior") } } -#' @rdname priors + +#' @rdname bssm_prior #' @export -tnormal <- function(init, mean, sd, min = -Inf, max = Inf){ - - if(any(!is.numeric(init), !is.numeric(mean), !is.numeric(sd))) { +normal <- normal_prior + +#' @rdname bssm_prior +#' @export +tnormal_prior <- function(init, mean, sd, min = -Inf, max = Inf) { + + if (any(!is.numeric(init), !is.numeric(mean), !is.numeric(sd))) { stop("Parameters for priors must be numeric.") } + if (any(init < min) | any(init > max)) { + stop(paste("Initial value for parameter with truncated Normal is not", + "between the lower and upper bounds.", sep = " ")) + } + if (any(sd < 0)) { - stop("Standard deviation parameter for Normal distribution must be positive.") + stop(paste("Standard deviation parameter for truncated Normal distribution", + "must be positive.", sep = " ")) } - + n <- max(length(init), length(mean), length(sd)) if (n > 1) { - structure(lapply(1:n, function(i) structure(list(prior_distribution = "tnormal", - init = safe_pick(init, i), mean = safe_pick(mean, i), sd = safe_pick(sd, i), - min = safe_pick(min, i), max = safe_pick(max, i)), + structure(lapply(1:n, function(i) + structure(list(prior_distribution = "tnormal", + init = safe_pick(init, i), mean = safe_pick(mean, i), + sd = safe_pick(sd, i), + min = safe_pick(min, i), max = safe_pick(max, i)), class = "bssm_prior")), class = "bssm_prior_list") } else { - structure(list(prior_distribution = "tnormal", init = init, mean = mean, sd = sd, - min = min, max = max), class = "bssm_prior") - } -} -combine_priors <- function(x) { - - if (length(x) == 0) return(list(prior_distributions = 0, parameters = matrix(0, 0, 0))) - - prior_distributions <- sapply(x, "[[", "prior_distribution") - parameters <- matrix(NA, 4, length(prior_distributions)) - for(i in 1:length(prior_distributions)) { - parameters[1:(length(x[[i]])-2), i] <- as.numeric(x[[i]][-(1:2)]) + structure(list(prior_distribution = "tnormal", init = init, mean = mean, + sd = sd, min = min, max = max), class = "bssm_prior") } - list(prior_distributions = - pmatch(prior_distributions, c("uniform", "halfnormal", "normal", "tnormal", "gamma"), duplicates.ok = TRUE)-1, - parameters = parameters) } -#' @rdname priors + +#' @rdname bssm_prior +#' @export +tnormal <- tnormal_prior + +#' @rdname bssm_prior #' @export -gamma <- function(init, shape, rate){ - - if(any(!is.numeric(init), !is.numeric(shape), !is.numeric(rate))) { +gamma_prior <- function(init, shape, rate) { + + if (any(!is.numeric(init), !is.numeric(shape), !is.numeric(rate))) { stop("Parameters for priors must be numeric.") } - if (any(shape < 0)) { + if (!all(shape > 0)) { stop("Shape parameter for Gamma distribution must be positive.") } - if (any(rate < 0)) { + if (!all(rate > 0)) { stop("Rate parameter for Gamma distribution must be positive.") } n <- max(length(init), length(shape), length(rate)) if (n > 1) { - structure(lapply(1:n, function(i) structure(list(prior_distribution = "gamma", - init = safe_pick(init, i), shape = safe_pick(shape, i), rate = safe_pick(rate, i)), - class = "bssm_prior")), class = "bssm_prior_list") - + structure(lapply(1:n, function(i) + structure(list(prior_distribution = "gamma", + init = safe_pick(init, i), shape = safe_pick(shape, i), + rate = safe_pick(rate, i)), + class = "bssm_prior")), class = "bssm_prior_list") + } else { - structure(list(prior_distribution = "gamma", init = init, shape = shape, rate = rate), + structure(list(prior_distribution = "gamma", init = init, + shape = shape, rate = rate), class = "bssm_prior") } } -is_prior <- function(x){ +#' @rdname bssm_prior +#' @export +gamma <- gamma_prior + +combine_priors <- function(x) { + + if (length(x) == 0) + return(list(prior_distributions = 0, parameters = matrix(0, 0, 0))) + + prior_distributions <- vapply(x, "[[", "prior_distribution", + FUN.VALUE = character(1)) + parameters <- matrix(NA, 4, length(prior_distributions)) + for (i in seq_along(prior_distributions)) { + parameters[1:(length(x[[i]]) - 2), i] <- as.numeric(x[[i]][-(1:2)]) + } + list(prior_distributions = + pmatch(prior_distributions, c("uniform", "halfnormal", "normal", + "tnormal", "gamma"), duplicates.ok = TRUE) - 1, + parameters = parameters) +} + + + + +is_prior <- function(x) { inherits(x, "bssm_prior") } -is_prior_list <- function(x){ +is_prior_list <- function(x) { inherits(x, "bssm_prior_list") } safe_pick <- function(x, i) { diff --git a/R/run_mcmc.R b/R/run_mcmc.R index a72c53dc..e9904c4e 100644 --- a/R/run_mcmc.R +++ b/R/run_mcmc.R @@ -1,105 +1,243 @@ #' Bayesian Inference of State Space Models #' -#' Adaptive Markov chain Monte Carlo simulation of state space models using -#' Robust Adaptive Metropolis algorithm by Vihola (2012). -#' See specific methods for various model types for details. +#' Adaptive Markov chain Monte Carlo simulation for SSMs using +#' Robust Adaptive Metropolis algorithm by Vihola (2012). Several different +#' MCMC sampling schemes are implemented, see parameter +#' arguments, package vignette, Vihola, Helske, Franks (2020) and Helske and +#' Vihola (2021) for details. +#' +#' @details +#' +#' For linear-Gaussian models, option \code{"summary"} does not simulate +#' states directly but computes the posterior means and variances of states +#' using fast Kalman smoothing. This is slightly faster, +#' more memory efficient and more accurate than calculations based on +#' simulation smoother. In other cases, the means and +#' covariances are computed using the full output of particle filter +#' instead of subsampling one of these as in case of +#' \code{output_type = "full"}. The states are sampled up to the time point n+1 +#' where n is the length of the input time series i.e. the last values are +#' one-step-ahead predictions. (for predicting further, see +#' \code{?predict.mcmc_output}). +#' +#' Initial values for the sampling are taken from the model object +#' (\code{model$theta}). If you want to continue from previous run, you can +#' reconstruct your original model by plugging in the previously obtained +#' parameters to \code{model$theta}, providing the S matrix for the RAM +#' algorithm and setting \code{burnin = 0}. See example. Note however, that +#' this is not identical as running all the iterations once, due to the +#' RNG "discontinuity" and because even without burnin bssm does include +#' "theta_0" i.e. the initial theta in the final chain (even with +#' \code{burnin=0}). #' #' @importFrom stats tsp -#' @param model State space model model of \code{bssm} package. -#' @param iter Number of MCMC iterations. -#' @param ... Parameters to specific methods. See \code{\link{run_mcmc.gaussian}}, -#' \code{\link{run_mcmc.nongaussian}}, \code{\link{run_mcmc.ssm_nlg}}, -#' and \code{\link{run_mcmc.ssm_sde}} for details. +#' @importFrom rlang is_interactive +#' @param model Model of class \code{bssm_model}. +#' @param iter A positive integer defining the total number of MCMC iterations. +#' Suitable value depends on the model, data, and the choice of specific +#' algorithms (\code{mcmc_type} and \code{sampling_method}). As increasing +#' \code{iter} also increases run time, it is is generally good idea to first +#' test the performance with a small values, e.g., less than 10000. +#' @param output_type Either \code{"full"} +#' (default, returns posterior samples from the posterior +#' \eqn{p(\alpha, \theta | y)}), \code{"theta"} (for marginal posterior of +#' theta), or \code{"summary"} (return the mean and variance estimates of the +#' states and posterior samples of theta). See details. +#' @param burnin A positive integer defining the length of the burn-in period +#' which is disregarded from the results. Defaults to \code{iter / 2}. +#' Note that all MCMC algorithms of \code{bssm} use adaptive MCMC during the +#' burn-in period in order to find good proposal distribution. +#' @param thin A positive integer defining the thinning rate. All the MCMC +#' algorithms in \code{bssm} use the jump chain representation (see refs), +#' and the thinning is applied to these blocks. Defaults to 1. +#' For IS-corrected methods, larger value can also be +#' statistically more effective. Note: With \code{output_type = "summary"}, +#' the thinning does not affect the computations of the summary statistics in +#' case of pseudo-marginal methods. +#' @param gamma Tuning parameter for the adaptation of RAM algorithm. Must be +#' between 0 and 1. +#' @param target_acceptance Target acceptance rate for MCMC. Defaults to 0.234. +#' Must be between 0 and 1. +#' @param S Matrix defining the initial value for the lower triangular matrix +#' of the RAM algorithm, so that the covariance matrix of the Gaussian proposal +#' distribution is \eqn{SS'}. Note that for some parameters +#' (currently the standard deviation, dispersion, and autoregressive parameters +#' of the BSM and AR(1) models) the sampling is done in unconstrained parameter +#' space, i.e. internal_theta = log(theta) (and logit(rho) or AR coefficient). +#' @param end_adaptive_phase Logical, if \code{TRUE}, S is held fixed after the +#' burnin period. Default is \code{FALSE}. +#' @param threads Number of threads for state simulation. Positive integer +#' (default is 1). +#' Note that parallel computing is only used in the post-correction phase of +#' IS-MCMC and when sampling the states in case of (approximate) Gaussian +#' models. +#' @param seed Seed for the C++ RNG (positive integer). +#' @param local_approx If \code{TRUE} (default), Gaussian approximation +#' needed for some of the methods is performed at each iteration. +#' If \code{FALSE}, approximation is updated only once at the start of the +#' MCMC using the initial model. +#' @param max_iter Maximum number of iterations used in Gaussian approximation, +#' as a positive integer. +#' Default is 100 (although typically only few iterations are needed). +#' @param conv_tol Positive tolerance parameter used in Gaussian approximation. +#' @param particles A positive integer defining the number of state samples per +#' MCMC iteration for models other than linear-Gaussian models. +#' Ignored if \code{mcmc_type} is \code{"approx"} or \code{"ekf"}. Suitable +#' values depend on the model, the data, \code{mcmc_type} and +#' \code{sampling_method}. While large values provide more +#' accurate estimates, the run time also increases with respect to to the +#' number of particles, so it is generally a good idea to test the run time +#' firstwith a small number of particles, e.g., less than 100. +#' @param mcmc_type What type of MCMC algorithm should be used for models other +#' than linear-Gaussian models? Possible choices are +#' \code{"pm"} for pseudo-marginal MCMC, +#' \code{"da"} for delayed acceptance version of PMCMC , +#' \code{"approx"} for approximate inference based on the Gaussian +#' approximation of the model, +#' \code{"ekf"} for approximate inference using extended Kalman filter +#' (for \code{ssm_nlg}), +#' or one of the three importance sampling type weighting schemes: +#' \code{"is3"} for simple importance sampling (weight is computed for each +#' MCMC iteration independently), +#' \code{"is2"} for jump chain importance sampling type weighting (default), or +#' \code{"is1"} for importance sampling type weighting where the number of +#' particles used for +#' weight computations is proportional to the length of the jump chain block. +#' @param sampling_method Method for state sampling when for models other than +#' linear-Gaussian models. If \code{"psi"}, \eqn{\psi}-APF is used (default). +#' If \code{"spdk"}, non-sequential importance sampling +#' based on Gaussian approximation is used. If \code{"bsf"}, bootstrap filter +#' is used. If \code{"ekf"}, particle filter based on EKF-proposals are used +#' (only for \code{ssm_nlg} models). +#' @param iekf_iter Non-negative integer. The default zero corresponds to +#' normal EKF, whereas \code{iekf_iter > 0} corresponds to iterated EKF +#' with \code{iekf_iter} iterations. Used only for models of class +#' \code{ssm_nlg}. +#' @param L_c,L_f For \code{ssm_sde} models, Positive integer values defining +#' the discretization levels for first and second stages (defined as 2^L). +#' For pseudo-marginal methods (\code{"pm"}), maximum of these is used. +#' @param verbose If \code{TRUE}, prints a progress bar to the console. If +#' missing, defined by \code{rlang::is_interactive}. +#' Set to \code{FALSE} if number of iterations is less than 50. +#' @param ... Ignored. +#' @return An object of class \code{mcmc_output}. #' @export +#' @srrstats {G2.3, G2.3a, G2.3b} match.arg and tolower used where applicable. +#' @srrstats {BS1.0, BS1.1, BS1.2, BS1.2a, BS1.2b} +#' @srrstats {BS2.6} +#' @srrstats {BS2.7} Illustrated in the examples. +#' @srrstats {BS2.7, BS1.3, BS1.3a, BS1.3b, BS2.8} Explained in docs. +#' @srrstats {BS2.9} The argument 'seed' is set to random value if not +#' specified by the user. +#' @srrstats {BS5.0, BS5.1, BS5.2} Starting values are integrated into the +#' input model, whereas some metadata (like the class of input model and seed) +#' is returned by run_mcmc. +#' @srrstats {BS2.12, BS2.13} There is a progress bar which can be switched off +#' with \code{verbose = FALSE}. +#' @srrstats {BS1.2c} Examples on defining priors. +#' @srrstats {BS2.14} No warnings are issues during MCMC. #' @rdname run_mcmc -#' @references Matti Vihola (2012). "Robust adaptive Metropolis algorithm with -#' coerced acceptance rate". Statistics and Computing, Volume 22, Issue 5, -#' pages 997--1008. -run_mcmc <- function(model, iter, ...) { +#' @references +#' Vihola M (2012). Robust adaptive Metropolis algorithm with +#' coerced acceptance rate. Statistics and Computing, 22(5), p 997-1008. +#' https://doi.org/10.1007/s11222-011-9269-5 +#' +#' Vihola, M, Helske, J, Franks, J (2020). Importance sampling type +#' estimators based on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 +#' +#' Helske J, Vihola M (2021). bssm: Bayesian Inference of Non-linear and +#' Non-Gaussian State Space Models in R. The R Journal (2021) 13:2, 578-589. +#' https://doi.org/10.32614/RJ-2021-103 +#' +run_mcmc <- function(model, ...) { UseMethod("run_mcmc", model) } -#' Bayesian Inference of Linear-Gaussian State Space Models -#' -#' @method run_mcmc gaussian -#' @rdname run_mcmc_g -#' @param model Model model. -#' @param iter Number of MCMC iterations. -#' @param output_type Type of output. Default is \code{"full"}, which returns -#' samples from the posterior \eqn{p(\alpha, \theta)}. Option \code{"summary"} does not simulate -#' states directly but computes the posterior means and variances of states using -#' fast Kalman smoothing. This is slightly faster, more memory efficient and -#' more accurate than calculations based on simulation smoother. Using option \code{"theta"} will only -#' return samples from the marginal posterior of the hyperparameters \eqn{\theta}. -#' @param burnin Length of the burn-in period which is disregarded from the -#' results. Defaults to \code{iter / 2}. Note that all MCMC algorithms of \code{bssm} -#' used adaptive MCMC during the burn-in period in order to find good proposal. -#' @param thin Thinning rate. All MCMC algorithms in \code{bssm} use the jump chain -#' representation, and the thinning is applied to these blocks. -#' Defaults to 1. -#' @param gamma Tuning parameter for the adaptation of RAM algorithm. Must be -#' between 0 and 1 (not checked). -#' @param target_acceptance Target acceptance rate for MCMC. Defaults to 0.234. -#' @param S Initial value for the lower triangular matrix of RAM -#' algorithm, so that the covariance matrix of the Gaussian proposal -#' distribution is \eqn{SS'}. Note that for some parameters -#' (currently the standard deviation and dispersion parameters of bsm_lg models) the sampling -#' is done for transformed parameters with internal_theta = log(theta). -#' @param end_adaptive_phase If \code{TRUE}, S is held fixed after the burnin period. Default is \code{FALSE}. -#' @param threads Number of threads for state simulation. The default is 1. -#' @param seed Seed for the random number generator. -#' @param ... Ignored. -#' @references -#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -#' Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +#' @method run_mcmc lineargaussian +#' @rdname run_mcmc #' @export -#' @examples -#' model <- ar1_lg(LakeHuron, rho = uniform(0.5,-1,1), -#' sigma = halfnormal(1, 10), mu = normal(500, 500, 500), +#' @examples +#' model <- ar1_lg(LakeHuron, rho = uniform(0.5,-1,1), +#' sigma = halfnormal(1, 10), mu = normal(500, 500, 500), #' sd_y = halfnormal(1, 10)) -#' +#' #' mcmc_results <- run_mcmc(model, iter = 2e4) #' summary(mcmc_results, return_se = TRUE) -#' -#' require("dplyr") -#' sumr <- as.data.frame(mcmc_results, variable = "states") %>% -#' group_by(time) %>% -#' summarise(mean = mean(value), -#' lwr = quantile(value, 0.025), -#' upr = quantile(value, 0.975)) -#' require("ggplot2") -#' sumr %>% ggplot(aes(time, mean)) + -#' geom_ribbon(aes(ymin = lwr, ymax = upr),alpha=0.25) + +#' +#' sumr <- summary(mcmc_results, variable = "states") +#' library("ggplot2") +#' ggplot(sumr, aes(time, Mean)) + +#' geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`), alpha = 0.25) + #' geom_line() + theme_bw() + -#' geom_point(data = data.frame(mean = LakeHuron, time = time(LakeHuron)), +#' geom_point(data = data.frame(Mean = LakeHuron, time = time(LakeHuron)), #' col = 2) -run_mcmc.gaussian <- function(model, iter, output_type = "full", - burnin = floor(iter / 2), thin = 1, gamma = 2/3, +#' +#' # Continue from the previous run +#' model$theta[] <- mcmc_results$theta[nrow(mcmc_results$theta), ] +#' run_more <- run_mcmc(model, S = mcmc_results$S, iter = 1000, burnin = 0) +#' +run_mcmc.lineargaussian <- function(model, iter, output_type = "full", + burnin = floor(iter / 2), thin = 1, gamma = 2 / 3, target_acceptance = 0.234, S, end_adaptive_phase = FALSE, threads = 1, - seed = sample(.Machine$integer.max, size = 1), ...) { - - - if(length(model$theta) == 0) stop("No unknown parameters ('model$theta' has length of zero).") + seed = sample(.Machine$integer.max, size = 1), + verbose, ...) { + + check_missingness(model) + + if (!test_flag(end_adaptive_phase)) + stop("Argument 'end_adaptive_phase' should be TRUE or FALSE. ") + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + + threads <- check_intmax(threads, "threads") + thin <- check_intmax(thin, "thin", max = 100) + iter <- check_intmax(iter, "iter", max = 1e12) + burnin <- check_intmax(burnin, "burnin", positive = FALSE, max = 1e12) + if(burnin > iter) stop("Argument 'burnin' should be smaller than 'iter'.") + + if (missing(verbose)) { + verbose <- is_interactive() + } else { + if (!test_flag(verbose)) + stop("Argument 'verbose' should be TRUE or FALSE. ") + } + if (iter < 50) verbose <- FALSE + + if (length(model$theta) == 0) + stop("No unknown parameters ('model$theta' has length of zero).") a <- proc.time() - - check_target(target_acceptance) - - output_type <- pmatch(output_type, c("full", "summary", "theta")) - + + check_prop(target_acceptance) + check_prop(gamma, "gamma") + output_type <- pmatch(tolower(output_type), c("full", "summary", "theta")) + if (inherits(model, "bsm_lg")) { names_ind <- !model$fixed & c(TRUE, TRUE, model$slope, model$seasonal) - model$theta[c("sd_y", "sd_level", "sd_slope", "sd_seasonal")[names_ind]] <- - log(pmax(1e-8, model$theta[c("sd_y", "sd_level", "sd_slope", "sd_seasonal")[names_ind]])) + transformed <- + c("sd_y", "sd_level", "sd_slope", "sd_seasonal")[names_ind] + model$theta[transformed] <- log(pmax(0.001, model$theta[transformed])) + } else { + if (inherits(model, "ar1_lg")) { + transformed <- c("sigma", "sd_y") + model$theta[transformed] <- log(pmax(0.001, model$theta[transformed])) + } } - + if (missing(S)) { S <- diag(0.1 * pmax(0.1, abs(model$theta)), length(model$theta)) } - - + if(output_type == "full") { + nsamples <- + ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + length(model$a1) * (iter - burnin) / thin * target_acceptance + if (nsamples > 1e12) { + warning(paste("Number of state samples to be stored is approximately", + nsamples, "you might run out of memory.")) + } + } out <- gaussian_mcmc(model, output_type, iter, burnin, thin, gamma, target_acceptance, S, seed, - end_adaptive_phase, threads, model_type(model)) - + end_adaptive_phase, threads, model_type(model), verbose) + if (output_type == 1) { colnames(out$alpha) <- names(model$a1) } else { @@ -110,12 +248,16 @@ run_mcmc.gaussian <- function(model, iter, output_type = "full", frequency = frequency(model$y)) } } - - colnames(out$theta) <- rownames(out$S) <- colnames(out$S) <- names(model$theta) - + + colnames(out$theta) <- rownames(out$S) <- colnames(out$S) <- + names(model$theta) + if (inherits(model, "bsm_lg")) { - out$theta[, c("sd_y", "sd_level", "sd_slope", "sd_seasonal")[names_ind]] <- - exp(out$theta[, c("sd_y", "sd_level", "sd_slope", "sd_seasonal")[names_ind]]) + out$theta[, transformed] <- exp(out$theta[, transformed]) + } else { + if (inherits(model, "ar1_lg")) { + out$theta[, transformed] <- exp(out$theta[, transformed]) + } } out$call <- match.call() out$seed <- seed @@ -124,257 +266,260 @@ run_mcmc.gaussian <- function(model, iter, output_type = "full", out$thin <- thin out$mcmc_type <- "gaussian_mcmc" out$output_type <- output_type + dim(out$counts) <- NULL out$time <- proc.time() - a class(out) <- "mcmc_output" attr(out, "model_type") <- class(model)[1] - attr(out, "ts") <- - list(start = start(model$y), end = end(model$y), frequency=frequency(model$y)) + attr(out, "ts") <- + list(start = start(model$y), end = end(model$y), + frequency = frequency(model$y)) out } - - -#' Bayesian Inference of Non-Gaussian State Space Models -#' -#' Methods for posterior inference of states and parameters. -#' #' @method run_mcmc nongaussian -#' @rdname run_mcmc_ng +#' @rdname run_mcmc #' @export -#' @param model Model model. -#' @param iter Number of MCMC iterations. -#' @param particles Number of state samples per MCMC iteration. -#' Ignored if \code{mcmc_type} is \code{"approx"}. -#' @param output_type Either \code{"full"} -#' (default, returns posterior samples of states alpha and hyperparameters theta), -#' \code{"theta"} (for marginal posterior of theta), -#' or \code{"summary"} (return the mean and variance estimates of the states -#' and posterior samples of theta). In case of \code{"summary"}, means and -#' covariances are computed using the full output of particle filter -#' instead of sampling one of these as in case of \code{output_type = "full"}. -#' @param mcmc_type What MCMC algorithm to use? Possible choices are -#' \code{"pm"} for pseudo-marginal MCMC, -#' \code{"da"} for delayed acceptance version of PMCMC , -#' \code{"approx"} for approximate inference based on the Gaussian approximation of the model, -#' or one of the three importance sampling type weighting schemes: -#' \code{"is3"} for simple importance sampling (weight is computed for each MCMC iteration independently), -#' \code{"is2"} for jump chain importance sampling type weighting (default), or -#' \code{"is1"} for importance sampling type weighting where the number of particles used for -#' weight computations is proportional to the length of the jump chain block. -#' @param sampling_method If \code{"psi"}, \eqn{\psi}-APF is used for state sampling -#' (default). If \code{"spdk"}, non-sequential importance sampling based -#' on Gaussian approximation is used. If \code{"bsf"}, bootstrap filter -#' is used. -#' @param burnin Length of the burn-in period which is disregarded from the -#' results. Defaults to \code{iter / 2}. -#' @param thin Thinning rate. Defaults to 1. Increase for large models in -#' order to save memory. For IS-corrected methods, larger -#' value can also be statistically more effective. -#' Note: With \code{output_type = "summary"}, the thinning does not affect the computations -#' of the summary statistics in case of pseudo-marginal methods. -#' @param gamma Tuning parameter for the adaptation of RAM algorithm. Must be -#' between 0 and 1 (not checked). -#' @param target_acceptance Target acceptance rate for MCMC. Defaults to 0.234. -#' For DA-MCMC, this corresponds to first stage acceptance rate, i.e., the total acceptance -#' rate will be smaller. -#' @param S Initial value for the lower triangular matrix of RAM -#' algorithm, so that the covariance matrix of the Gaussian proposal -#' distribution is \eqn{SS'}. Note that for some parameters -#' (currently the standard deviation and dispersion parameters of bsm_ng models) the sampling -#' is done for transformed parameters with internal_theta = log(theta). -#' @param end_adaptive_phase If \code{TRUE}, S is held fixed after the burnin period. Default is \code{FALSE}. -#' @param local_approx If \code{TRUE} (default), Gaussian approximation needed for -#' importance sampling is performed at each iteration. If \code{FALSE}, approximation is updated only -#' once at the start of the MCMC using the initial model. -#' @param threads Number of threads for state simulation. The default is 1. -#' Note that parallel computing is only used in the post-correction phase of IS-MCMC -#' and when sampling the states in case of approximate models. -#' @param seed Seed for the random number generator. -#' @param max_iter Maximum number of iterations used in Gaussian approximation. -#' @param conv_tol Tolerance parameter used in Gaussian approximation. -#' @param ... Ignored. #' @examples #' set.seed(1) -#' n <- 50 +#' n <- 50 #' slope <- cumsum(c(0, rnorm(n - 1, sd = 0.001))) #' level <- cumsum(slope + c(0, rnorm(n - 1, sd = 0.2))) #' y <- rpois(n, exp(level)) -#' poisson_model <- bsm_ng(y, -#' sd_level = halfnormal(0.01, 1), -#' sd_slope = halfnormal(0.01, 0.1), +#' poisson_model <- bsm_ng(y, +#' sd_level = halfnormal(0.01, 1), +#' sd_slope = halfnormal(0.01, 0.1), #' P1 = diag(c(10, 0.1)), distribution = "poisson") -#' +#' #' # Note small number of iterations for CRAN checks -#' mcmc_out <- run_mcmc(poisson_model, iter = 1000, particles = 10, +#' mcmc_out <- run_mcmc(poisson_model, iter = 1000, particles = 10, #' mcmc_type = "da") #' summary(mcmc_out, what = "theta", return_se = TRUE) -#' +#' #' set.seed(123) #' n <- 50 #' sd_level <- 0.1 #' drift <- 0.01 #' beta <- -0.9 #' phi <- 5 -#' +#' #' level <- cumsum(c(5, drift + rnorm(n - 1, sd = sd_level))) #' x <- 3 + (1:n) * drift + sin(1:n + runif(n, -1, 1)) #' y <- rnbinom(n, size = phi, mu = exp(beta * x + level)) -#' +#' #' model <- bsm_ng(y, xreg = x, #' beta = normal(0, 0, 10), #' phi = halfnormal(1, 10), -#' sd_level = halfnormal(0.1, 1), +#' sd_level = halfnormal(0.1, 1), #' sd_slope = halfnormal(0.01, 0.1), -#' a1 = c(0, 0), P1 = diag(c(10, 0.1)^2), +#' a1 = c(0, 0), P1 = diag(c(10, 0.1)^2), #' distribution = "negative binomial") -#' +#' #' # run IS-MCMC #' # Note small number of iterations for CRAN checks -#' fit <- run_mcmc(model, iter = 5000, +#' fit <- run_mcmc(model, iter = 4000, #' particles = 10, mcmc_type = "is2", seed = 1) #' -#' # extract states +#' # extract states #' d_states <- as.data.frame(fit, variable = "states", time = 1:n) -#' +#' #' library("dplyr") #' library("ggplot2") -#' +#' #' # compute summary statistics -#' level_sumr <- d_states %>% -#' filter(variable == "level") %>% -#' group_by(time) %>% -#' summarise(mean = Hmisc::wtd.mean(value, weight, normwt = TRUE), -#' lwr = Hmisc::wtd.quantile(value, weight, -#' 0.025, normwt = TRUE), -#' upr = Hmisc::wtd.quantile(value, weight, -#' 0.975, normwt = TRUE)) -#' +#' level_sumr <- d_states |> +#' filter(variable == "level") |> +#' group_by(time) |> +#' summarise(mean = diagis::weighted_mean(value, weight), +#' lwr = diagis::weighted_quantile(value, weight, +#' 0.025), +#' upr = diagis::weighted_quantile(value, weight, +#' 0.975)) +#' #' # visualize -#' level_sumr %>% ggplot(aes(x = time, y = mean)) + +#' level_sumr |> ggplot(aes(x = time, y = mean)) + #' geom_line() + #' geom_line(aes(y = lwr), linetype = "dashed", na.rm = TRUE) + #' geom_line(aes(y = upr), linetype = "dashed", na.rm = TRUE) + -#' theme_bw() + -#' theme(legend.title = element_blank()) + +#' theme_bw() + +#' theme(legend.title = element_blank()) + #' xlab("Time") + ylab("Level") -#' +#' #' # theta #' d_theta <- as.data.frame(fit, variable = "theta") -#' ggplot(d_theta, aes(x = value)) + -#' geom_density(aes(weight = weight), adjust = 2, fill = "#92f0a8") + -#' facet_wrap(~ variable, scales = "free") + +#' ggplot(d_theta, aes(x = value)) + +#' geom_density(aes(weight = weight), adjust = 2, fill = "#92f0a8") + +#' facet_wrap(~ variable, scales = "free") + #' theme_bw() -#' -#' +#' +#' #' # Bivariate Poisson model: -#' +#' #' set.seed(1) #' x <- cumsum(c(3, rnorm(19, sd = 0.5))) #' y <- cbind( -#' rpois(20, exp(x)), +#' rpois(20, exp(x)), #' rpois(20, exp(x))) -#' +#' #' prior_fn <- function(theta) { #' # half-normal prior using transformation #' dnorm(exp(theta), 0, 1, log = TRUE) + theta # plus jacobian term #' } -#' +#' #' update_fn <- function(theta) { #' list(R = array(exp(theta), c(1, 1, 1))) #' } -#' -#' model <- ssm_mng(y = y, Z = matrix(1,2,1), T = 1, +#' +#' model <- ssm_mng(y = y, Z = matrix(1,2,1), T = 1, #' R = 0.1, P1 = 1, distribution = "poisson", -#' init_theta = log(0.1), +#' init_theta = log(0.1), #' prior_fn = prior_fn, update_fn = update_fn) -#' +#' #' # Note small number of iterations for CRAN checks -#' out <- run_mcmc(model, iter = 5000, mcmc_type = "approx") -#' -#' sumr <- as.data.frame(out, variable = "states") %>% -#' group_by(time) %>% mutate(value = exp(value)) %>% -#' summarise(mean = mean(value), +#' out <- run_mcmc(model, iter = 4000, mcmc_type = "approx") +#' +#' sumr <- as.data.frame(out, variable = "states") |> +#' group_by(time) |> mutate(value = exp(value)) |> +#' summarise(mean = mean(value), #' ymin = quantile(value, 0.05), ymax = quantile(value, 0.95)) -#' ggplot(sumr, aes(time, mean)) + -#' geom_ribbon(aes(ymin = ymin, ymax = ymax),alpha = 0.25) + -#' geom_line() + -#' geom_line(data = data.frame(mean = y[, 1], time = 1:20), colour = "tomato") + -#' geom_line(data = data.frame(mean = y[, 2], time = 1:20), colour = "tomato") + +#' ggplot(sumr, aes(time, mean)) + +#' geom_ribbon(aes(ymin = ymin, ymax = ymax),alpha = 0.25) + +#' geom_line() + +#' geom_line(data = data.frame(mean = y[, 1], time = 1:20), +#' colour = "tomato") + +#' geom_line(data = data.frame(mean = y[, 2], time = 1:20), +#' colour = "tomato") + #' theme_bw() -#' +#' run_mcmc.nongaussian <- function(model, iter, particles, output_type = "full", - mcmc_type = "is2", sampling_method = "psi", burnin = floor(iter/2), - thin = 1, gamma = 2/3, target_acceptance = 0.234, S, end_adaptive_phase = FALSE, - local_approx = TRUE, threads = 1, - seed = sample(.Machine$integer.max, size = 1), max_iter = 100, conv_tol = 1e-8, ...) { - - if(missing(particles)) { + mcmc_type = "is2", sampling_method = "psi", burnin = floor(iter / 2), + thin = 1, gamma = 2 / 3, target_acceptance = 0.234, S, + end_adaptive_phase = FALSE, local_approx = TRUE, threads = 1, + seed = sample(.Machine$integer.max, size = 1), max_iter = 100, + conv_tol = 1e-8, verbose, ...) { + + check_missingness(model) + + if (!test_flag(end_adaptive_phase)) + stop("Argument 'end_adaptive_phase' should be TRUE or FALSE. ") + + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim + particles <- check_intmax(particles, "particles") } + } else { + particles <- check_intmax(particles, "particles") } - - if(length(model$theta) == 0) stop("No unknown parameters ('model$theta' has length of zero).") + + threads <- check_intmax(threads, "threads") + model$max_iter <- check_intmax(max_iter, "max_iter", positive = FALSE) + model$conv_tol <- check_positive_real(conv_tol, "conv_tol") + thin <- check_intmax(thin, "thin", max = 100) + iter <- check_intmax(iter, "iter", max = 1e12) + burnin <- check_intmax(burnin, "burnin", positive = FALSE, max = 1e12) + if(burnin > iter) stop("Argument 'burnin' should be smaller than 'iter'.") + + if (missing(verbose)) { + verbose <- is_interactive() + } else { + if (!test_flag(verbose)) + stop("Argument 'verbose' should be TRUE or FALSE. ") + } + if (iter < 50) verbose <- FALSE + + if (!test_flag(local_approx)) { + stop("Argument 'local_approx' should be TRUE or FALSE. ") + } else model$local_approx <- local_approx + + if (length(model$theta) == 0) + stop("No unknown parameters ('model$theta' has length of zero).") a <- proc.time() - check_target(target_acceptance) - - output_type <- pmatch(output_type, c("full", "summary", "theta")) - mcmc_type <- match.arg(mcmc_type, c("pm", "da", paste0("is", 1:3), "approx")) + check_prop(target_acceptance) + check_prop(gamma, "gamma") + + output_type <- pmatch(tolower(output_type), c("full", "summary", "theta")) + mcmc_type <- match.arg(tolower(mcmc_type), + c("pm", "da", paste0("is", 1:3), "approx")) if (mcmc_type == "approx") particles <- 0 - if (particles < 2 && mcmc_type != "approx") - stop("Number of state samples less than 2, use 'mcmc_type' 'approx' instead.") - - sampling_method <- pmatch(match.arg(sampling_method, c("psi", "bsf", "spdk")), - c("psi", "bsf", "spdk")) - - model$max_iter <- max_iter - model$conv_tol <- conv_tol - model$local_approx <- local_approx - - if(inherits(model, "bsm_ng")) { + if (particles < 2 && mcmc_type != "approx") + stop(paste("Number of state samples less than 2, use 'mcmc_type' 'approx'", + "instead.", sep = " ")) + + sampling_method <- + pmatch(match.arg(tolower(sampling_method), c("psi", "bsf", "spdk")), + c("psi", "bsf", "spdk")) + + dists <- + c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian") + model$distribution <- + pmatch(model$distribution, dists, duplicates.ok = TRUE) - 1 + + if(output_type == "full") { + nsamples <- + ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + length(model$a1) * (iter - burnin) / thin * target_acceptance + if (nsamples > 1e12) { + warning(paste("Number of state samples to be stored is approximately", + nsamples, "you might run out of memory.")) + } + } + + if (inherits(model, "bsm_ng")) { + names_ind <- c(!model$fixed & c(TRUE, model$slope, model$seasonal), model$noise) - transformed <- c(c("sd_level", "sd_slope", "sd_seasonal", "sd_noise")[names_ind], - if (model$distribution == "negative binomial") "phi") - model$theta[transformed] <- log(pmax(1e-8, model$theta[transformed])) + + transformed <- c( + c("sd_level", "sd_slope", "sd_seasonal", "sd_noise")[names_ind], + if (dists[model$distribution + 1] %in% dists[4:5]) "phi") + + model$theta[transformed] <- log(pmax(0.001, model$theta[transformed])) + } else { + if (inherits(model, "ar1_ng")) { + + transformed <- c("sigma", + if (dists[model$distribution + 1] %in% dists[4:5]) "phi") + + model$theta[transformed] <- log(pmax(0.001, model$theta[transformed])) + } } - + if (missing(S)) { S <- diag(0.1 * pmax(0.1, abs(model$theta)), length(model$theta)) } - - model$distribution <- pmatch(model$distribution, - c("svm", "poisson", "binomial", "negative binomial", "gamma", "gaussian"), - duplicates.ok = TRUE) - 1 - + switch(mcmc_type, "da" = { - out <- nongaussian_da_mcmc(model, + out <- nongaussian_da_mcmc(model, output_type, particles, iter, burnin, thin, gamma, target_acceptance, S, seed, end_adaptive_phase, threads, - sampling_method, model_type(model)) + sampling_method, model_type(model), verbose) }, "pm" = { out <- nongaussian_pm_mcmc(model, output_type, particles, iter, burnin, thin, gamma, target_acceptance, S, - seed, end_adaptive_phase, threads, - sampling_method, model_type(model)) + seed, end_adaptive_phase, threads, + sampling_method, model_type(model), verbose) }, "is1" =, "is2" =, "is3" = { out <- nongaussian_is_mcmc(model, output_type, particles, iter, burnin, thin, gamma, target_acceptance, S, - seed, end_adaptive_phase, threads, + seed, end_adaptive_phase, threads, sampling_method, - pmatch(mcmc_type, paste0("is", 1:3)), model_type(model), FALSE) + pmatch(mcmc_type, paste0("is", 1:3)), model_type(model), FALSE, + verbose) }, "approx" = { out <- nongaussian_is_mcmc(model, output_type, particles, iter, burnin, thin, gamma, target_acceptance, S, - seed, end_adaptive_phase, threads, - sampling_method, 2, model_type(model), TRUE) + seed, end_adaptive_phase, threads, + sampling_method, 2, model_type(model), TRUE, verbose) }) if (output_type == 1) { colnames(out$alpha) <- names(model$a1) @@ -386,10 +531,15 @@ run_mcmc.nongaussian <- function(model, iter, particles, output_type = "full", frequency = frequency(model$y)) } } - - colnames(out$theta) <- rownames(out$S) <- colnames(out$S) <- names(model$theta) - if(inherits(model, "bsm_ng")) { + + colnames(out$theta) <- rownames(out$S) <- colnames(out$S) <- + names(model$theta) + if (inherits(model, "bsm_ng")) { out$theta[, transformed] <- exp(out$theta[, transformed]) + } else { + if (inherits(model, "ar1_ng")) { + out$theta[, transformed] <- exp(out$theta[, transformed]) + } } out$iter <- iter out$burnin <- burnin @@ -398,104 +548,93 @@ run_mcmc.nongaussian <- function(model, iter, particles, output_type = "full", out$output_type <- output_type out$call <- match.call() out$seed <- seed + dim(out$counts) <- NULL out$time <- proc.time() - a class(out) <- "mcmc_output" attr(out, "model_type") <- class(model)[1] - attr(out, "ts") <- - list(start = start(model$y), end = end(model$y), frequency=frequency(model$y)) + attr(out, "ts") <- + list(start = start(model$y), end = end(model$y), + frequency = frequency(model$y)) out } -#' Bayesian Inference of non-linear state space models -#' -#' Methods for posterior inference of states and parameters. -#' #' @method run_mcmc ssm_nlg -#' @param model Model model. -#' @param iter Number of MCMC iterations. -#' @param particles Number of state samples per MCMC iteration. -#' Ignored if \code{mcmc_type} is \code{"approx"} or \code{"ekf"}. -#' @param output_type Either \code{"full"} -#' (default, returns posterior samples of states alpha and hyperparameters theta), -#' \code{"theta"} (for marginal posterior of theta), -#' or \code{"summary"} (return the mean and variance estimates of the states -#' and posterior samples of theta). In case of \code{"summary"}, means and -#' covariances are computed using the full output of particle filter -#' instead of sampling one of these as in case of \code{output_type = "full"}. -#' @param mcmc_type What MCMC algorithm to use? Possible choices are -#' \code{"pm"} for pseudo-marginal MCMC, -#' \code{"da"} for delayed acceptance version of pseudo-marginal MCMC, -#' \code{"approx"} for approximate inference based on the Gaussian approximation of the model, -#' \code{"ekf"} for approximate inference using extended Kalman filter, -#' or one of the three importance sampling type weighting schemes: -#' \code{"is3"} for simple importance sampling (weight is computed for each MCMC iteration independently), -#' \code{"is2"} for jump chain importance sampling type weighting (default), or -#' \code{"is1"} for importance sampling type weighting where the number of particles used for -#' weight computations is proportional to the length of the jump chain block. -#' @param sampling_method If \code{"bsf"} (default), bootstrap filter is used for state sampling. -#' If \code{"ekf"}, particle filter based on EKF-proposals are used. -#' If \code{"psi"}, \eqn{\psi}-APF is used. -#' @param burnin Length of the burn-in period which is disregarded from the -#' results. Defaults to \code{iter / 2}. -#' @param thin Thinning rate. Defaults to 1. Increase for large models in -#' order to save memory. For IS-corrected methods, larger -#' value can also be statistically more effective. -#' Note: With \code{output_type = "summary"}, the thinning does not affect the computations -#' of the summary statistics in case of pseudo-marginal methods. -#' @param gamma Tuning parameter for the adaptation of RAM algorithm. Must be -#' between 0 and 1 (not checked). -#' @param target_acceptance Target acceptance ratio for RAM. Defaults to 0.234. -#' For DA-MCMC, this corresponds to first stage acceptance rate, i.e., the total acceptance -#' rate will be smaller. -#' @param S Initial value for the lower triangular matrix of RAM -#' algorithm, so that the covariance matrix of the Gaussian proposal -#' distribution is \eqn{SS'}. Note that for some parameters -#' (currently the standard deviation and dispersion parameters of bsm_ng models) the sampling -#' is done for transformed parameters with internal_theta = log(theta). -#' @param end_adaptive_phase If \code{TRUE}, S is held fixed after the burnin period. Default is \code{FALSE}. -#' @param threads Number of threads for state simulation. -#' @param seed Seed for the random number generator. -#' @param max_iter Maximum number of iterations used in Gaussian approximation. -#' @param conv_tol Tolerance parameter used in Gaussian approximation. -#' @param iekf_iter If \code{iekf_iter > 0}, iterated extended Kalman filter is used with -#' \code{iekf_iter} iterations in place of standard EKF. Defaults to zero. -#' @param ... Ignored. +#' @rdname run_mcmc #' @export -#' @references -#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -#' Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 run_mcmc.ssm_nlg <- function(model, iter, particles, output_type = "full", mcmc_type = "is2", sampling_method = "bsf", - burnin = floor(iter/2), thin = 1, - gamma = 2/3, target_acceptance = 0.234, S, end_adaptive_phase = FALSE, + burnin = floor(iter / 2), thin = 1, + gamma = 2 / 3, target_acceptance = 0.234, S, end_adaptive_phase = FALSE, threads = 1, seed = sample(.Machine$integer.max, size = 1), max_iter = 100, - conv_tol = 1e-8, iekf_iter = 0, ...) { - - if(missing(particles)) { + conv_tol = 1e-8, iekf_iter = 0, verbose, ...) { + + check_missingness(model) + + if (!test_flag(end_adaptive_phase)) + stop("Argument 'end_adaptive_phase' should be TRUE or FALSE. ") + + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim + particles <- check_intmax(particles, "particles") } + } else { + particles <- check_intmax(particles, "particles") + } + + threads <- check_intmax(threads, "threads") + max_iter <- check_intmax(max_iter, "max_iter", positive = FALSE) + conv_tol <- check_positive_real(conv_tol, "conv_tol") + thin <- check_intmax(thin, "thin", max = 100) + iter <- check_intmax(iter, "iter", max = 1e12) + burnin <- check_intmax(burnin, "burnin", positive = FALSE, max = 1e12) + iekf_iter <- check_intmax(iekf_iter, "iekf_iter", positive = FALSE) + if(burnin > iter) stop("Argument 'burnin' should be smaller than 'iter'.") + + if (missing(verbose)) { + verbose <- is_interactive() + } else { + if (!test_flag(verbose)) + stop("Argument 'verbose' should be TRUE or FALSE. ") } - - if(length(model$theta) == 0) stop("No unknown parameters ('model$theta' has length of zero).") + if (iter < 50) verbose <- FALSE + + if (length(model$theta) == 0) + stop("No unknown parameters ('model$theta' has length of zero).") a <- proc.time() - check_target(target_acceptance) - - output_type <- pmatch(output_type, c("full", "summary", "theta")) - mcmc_type <- match.arg(mcmc_type, c("pm", "da", paste0("is", 1:3), "ekf", "approx")) - if(mcmc_type %in% c("ekf", "approx")) particles <- 0 - sampling_method <- pmatch(match.arg(sampling_method, c("psi", "bsf", "ekf")), - c("psi", "bsf", NA, "ekf")) - + + check_prop(target_acceptance) + check_prop(gamma, "gamma") + + output_type <- pmatch(tolower(output_type), c("full", "summary", "theta")) + mcmc_type <- match.arg(tolower(mcmc_type), c("pm", "da", paste0("is", 1:3), + "ekf", "approx")) + if (mcmc_type %in% c("ekf", "approx")) particles <- 0 + sampling_method <- pmatch(match.arg(tolower(sampling_method), + c("psi", "bsf", "ekf")), c("psi", "bsf", NA, "ekf")) + if (missing(S)) { S <- diag(0.1 * pmax(0.1, abs(model$theta)), length(model$theta)) } - + if (particles < 2 && !(mcmc_type %in% c("ekf", "approx"))) - stop("Number of state samples less than 2, use 'mcmc_type' 'approx' or 'ekf' instead.") - - + stop(paste("Number of state samples less than 2, use 'mcmc_type'", + "'approx' or 'ekf' instead.", sep = " ")) + + if(output_type == "full") { + nsamples <- + ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + model$n_states * (iter - burnin) / thin * target_acceptance + if (nsamples > 1e12) { + warning(paste("Number of state samples to be stored is approximately", + nsamples, "you might run out of memory.")) + } + } + out <- switch(mcmc_type, "da" = { nonlinear_da_mcmc(t(model$y), model$Z, model$H, model$T, @@ -505,7 +644,7 @@ run_mcmc.ssm_nlg <- function(model, iter, particles, output_type = "full", model$n_states, model$n_etas, seed, particles, iter, burnin, thin, gamma, target_acceptance, S, end_adaptive_phase, threads, max_iter, conv_tol, - sampling_method,iekf_iter, output_type) + sampling_method, iekf_iter, output_type, verbose) }, "pm" = { nonlinear_pm_mcmc(t(model$y), model$Z, model$H, model$T, @@ -515,7 +654,7 @@ run_mcmc.ssm_nlg <- function(model, iter, particles, output_type = "full", model$n_states, model$n_etas, seed, particles, iter, burnin, thin, gamma, target_acceptance, S, end_adaptive_phase, threads, max_iter, conv_tol, - sampling_method,iekf_iter, output_type) + sampling_method, iekf_iter, output_type, verbose) }, "is1" =, "is2" =, @@ -529,8 +668,8 @@ run_mcmc.ssm_nlg <- function(model, iter, particles, output_type = "full", model$n_states, model$n_etas, seed, particles, iter, burnin, thin, gamma, target_acceptance, S, end_adaptive_phase, threads, pmatch(mcmc_type, paste0("is", 1:3)), - sampling_method, max_iter, conv_tol, iekf_iter, - output_type, FALSE) + sampling_method, max_iter, conv_tol, iekf_iter, + output_type, FALSE, verbose) }, "ekf" = { nonlinear_ekf_mcmc(t(model$y), model$Z, model$H, model$T, @@ -539,7 +678,7 @@ run_mcmc.ssm_nlg <- function(model, iter, particles, output_type = "full", model$known_tv_params, as.integer(model$time_varying), model$n_states, model$n_etas, seed, iter, burnin, thin, gamma, target_acceptance, S, - end_adaptive_phase, threads, iekf_iter, output_type) + end_adaptive_phase, threads, iekf_iter, output_type, verbose) }, "approx" = { nonlinear_is_mcmc(t(model$y), model$Z, model$H, model$T, @@ -549,8 +688,8 @@ run_mcmc.ssm_nlg <- function(model, iter, particles, output_type = "full", model$n_states, model$n_etas, seed, particles, iter, burnin, thin, gamma, target_acceptance, S, end_adaptive_phase, threads, 2, - sampling_method, max_iter, conv_tol, - iekf_iter, output_type, TRUE) + sampling_method, max_iter, conv_tol, + iekf_iter, output_type, TRUE, verbose) } ) if (output_type == 1) { @@ -563,10 +702,11 @@ run_mcmc.ssm_nlg <- function(model, iter, particles, output_type = "full", frequency = frequency(model$y)) } } - - - colnames(out$theta) <- rownames(out$S) <- colnames(out$S) <- names(model$theta) - + + + colnames(out$theta) <- rownames(out$S) <- colnames(out$S) <- + names(model$theta) + out$iter <- iter out$burnin <- burnin out$thin <- thin @@ -574,133 +714,131 @@ run_mcmc.ssm_nlg <- function(model, iter, particles, output_type = "full", out$output_type <- output_type out$call <- match.call() out$seed <- seed + dim(out$counts) <- NULL out$time <- proc.time() - a class(out) <- "mcmc_output" attr(out, "model_type") <- "ssm_nlg" - attr(out, "ts") <- - list(start = start(model$y), end = end(model$y), frequency=frequency(model$y)) + attr(out, "ts") <- + list(start = start(model$y), end = end(model$y), + frequency = frequency(model$y)) out } -#' Bayesian Inference of SDE -#' -#' Methods for posterior inference of states and parameters. -#' #' @method run_mcmc ssm_sde -#' @param model Model model. -#' @param iter Number of MCMC iterations. -#' @param particles Number of state samples per MCMC iteration. -#' @param output_type Either \code{"full"} -#' (default, returns posterior samples of states alpha and hyperparameters theta), -#' \code{"theta"} (for marginal posterior of theta), -#' or \code{"summary"} (return the mean and variance estimates of the states -#' and posterior samples of theta). In case of \code{"summary"}, means and -#' covariances are computed using the full output of particle filter -#' instead of sampling one of these as in case of \code{output_type = "full"}. -#' If \code{particles = 0}, this is argument ignored and set to \code{"theta"}. -#' @param mcmc_type What MCMC algorithm to use? Possible choices are -#' \code{"pm"} for pseudo-marginal MCMC, -#' \code{"da"} for delayed acceptance version of pseudo-marginal MCMC, -#' or one of the three importance sampling type weighting schemes: -#' \code{"is3"} for simple importance sampling (weight is computed for each MCMC iteration independently), -#' \code{"is2"} for jump chain importance sampling type weighting (default), or -#' \code{"is1"} for importance sampling type weighting where the number of particles used for -#' weight computations is proportional to the length of the jump chain block. -#' @param burnin Length of the burn-in period which is disregarded from the -#' results. Defaults to \code{iter / 2}. -#' @param thin Thinning rate. Defaults to 1. Increase for large models in -#' order to save memory. For IS-corrected methods, larger -#' value can also be statistically more effective. -#' Note: With \code{output_type = "summary"}, the thinning does not affect the computations -#' of the summary statistics in case of pseudo-marginal methods. -#' @param gamma Tuning parameter for the adaptation of RAM algorithm. Must be -#' between 0 and 1 (not checked). -#' @param target_acceptance Target acceptance ratio for RAM. Defaults to 0.234. -#' For DA-MCMC, this corresponds to first stage acceptance rate, i.e., the total acceptance -#' rate will be smaller. -#' @param S Initial value for the lower triangular matrix of RAM -#' algorithm, so that the covariance matrix of the Gaussian proposal -#' distribution is \eqn{SS'}. Note that for some parameters -#' (currently the standard deviation and dispersion parameters of bsm_ng models) the sampling -#' is done for transformed parameters with internal_theta = log(theta). -#' @param end_adaptive_phase If \code{TRUE}, S is held fixed after the burnin period. Default is \code{FALSE}. -#' @param threads Number of threads for state simulation. -#' @param L_c,L_f Integer values defining the discretization levels for first and second stages (defined as 2^L). -#' For PM methods, maximum of these is used. -#' @param seed Seed for the random number generator. -#' @param ... Ignored. +#' @rdname run_mcmc #' @export -#' @references -#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -#' Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +#' @references +#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based +#' on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 2020; 1-38. https://doi.org/10.1111/sjos.12492 run_mcmc.ssm_sde <- function(model, iter, particles, output_type = "full", mcmc_type = "is2", L_c, L_f, burnin = floor(iter/2), thin = 1, gamma = 2/3, target_acceptance = 0.234, S, end_adaptive_phase = FALSE, - threads = 1, seed = sample(.Machine$integer.max, size = 1), ...) { - - if(any(c(model$drift, model$diffusion, model$ddiffusion, - model$prior_pdf, model$obs_pdf) %in% c("", ""))) { - stop("NULL pointer detected, please recompile the pointer file and reconstruct the model.") + threads = 1, seed = sample(.Machine$integer.max, size = 1), verbose, + ...) { + + check_missingness(model) + + if (any(c(model$drift, model$diffusion, model$ddiffusion, model$prior_pdf, + model$obs_pdf) %in% c("", ""))) { + stop(paste("NULL pointer detected, please recompile the pointer file", + "and reconstruct the model.", sep = " ")) } - - if(missing(particles)) { + + if (!test_flag(end_adaptive_phase)) + stop("Argument 'end_adaptive_phase' should be TRUE or FALSE. ") + + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + + if (missing(particles)) { nsim <- eval(match.call(expand.dots = TRUE)$nsim) if (!is.null(nsim)) { - warning("Argument `nsim` is deprecated. Use argument `particles` instead.") + warning(paste("Argument `nsim` is deprecated. Use argument `particles`", + "instead.", sep = " ")) particles <- nsim } } - - if(length(model$theta) == 0) stop("No unknown parameters ('model$theta' has length of zero).") + particles <- check_intmax(particles, "particles") + threads <- check_intmax(threads, "threads") + thin <- check_intmax(thin, "thin", max = 100) + iter <- check_intmax(iter, "iter", max = 1e12) + burnin <- check_intmax(burnin, "burnin", positive = FALSE, max = 1e12) + if(burnin > iter) stop("Argument 'burnin' should be smaller than 'iter'.") + + if (missing(verbose)) { + verbose <- is_interactive() + } else { + if (!test_flag(verbose)) + stop("Argument 'verbose' should be TRUE or FALSE. ") + } + if (iter < 50) verbose <- FALSE + + if (length(model$theta) == 0) + stop("No unknown parameters ('model$theta' has length of zero).") a <- proc.time() - check_target(target_acceptance) - if(particles <= 0) stop("particles should be positive integer.") - - output_type <- pmatch(output_type, c("full", "summary", "theta")) - mcmc_type <- match.arg(mcmc_type, c("pm", "da", paste0("is", 1:3))) - + + check_prop(target_acceptance) + check_prop(gamma, "gamma") + + output_type <- pmatch(tolower(output_type), c("full", "summary", "theta")) + mcmc_type <- match.arg(tolower(mcmc_type), c("pm", "da", paste0("is", 1:3))) + if (missing(S)) { S <- diag(0.1 * pmax(0.1, abs(model$theta)), length(model$theta)) } - - if (mcmc_type == "da"){ + + if (mcmc_type != "pm") { if (L_f <= L_c) stop("L_f should be larger than L_c.") - if(L_c < 1) stop("L_c should be at least 1") - out <- sde_da_mcmc(model$y, model$x0, model$positive, - model$drift, model$diffusion, model$ddiffusion, - model$prior_pdf, model$obs_pdf, model$theta, - particles, L_c, L_f, seed, - iter, burnin, thin, gamma, target_acceptance, S, - end_adaptive_phase, output_type) + if (L_c < 1) stop("L_c should be at least 1") } else { - if(mcmc_type == "pm") { - if (missing(L_c)) L_c <- 0 - if (missing(L_f)) L_f <- 0 - L <- max(L_c, L_f) - if(L <= 0) stop("L should be positive.") + if (missing(L_c)) L_c <- 0 + if (missing(L_f)) L_f <- 0 + L <- max(L_c, L_f) + if (L <= 0) stop("L should be positive.") + } + if(output_type == "full") { + nsamples <- length(model$y) * (iter - burnin) / thin * target_acceptance + if (nsamples > 1e12) { + warning(paste("Number of state samples to be stored is approximately", + nsamples, "you might run out of memory.")) + } + } + + out <- switch(mcmc_type, + "da" = { + out <- sde_da_mcmc(model$y, model$x0, model$positive, + model$drift, model$diffusion, model$ddiffusion, + model$prior_pdf, model$obs_pdf, model$theta, + particles, L_c, L_f, seed, + iter, burnin, thin, gamma, target_acceptance, S, + end_adaptive_phase, output_type, verbose) + }, + "pm" = { + out <- sde_pm_mcmc(model$y, model$x0, model$positive, model$drift, model$diffusion, model$ddiffusion, model$prior_pdf, model$obs_pdf, model$theta, particles, L, seed, iter, burnin, thin, gamma, target_acceptance, S, - end_adaptive_phase, output_type) - } else { - if (L_f <= L_c) stop("L_f should be larger than L_c.") - if(L_c < 1) stop("L_c should be at least 1") - + end_adaptive_phase, output_type, verbose) + }, + "is1" =, + "is2" =, + "is3" = { out <- sde_is_mcmc(model$y, model$x0, model$positive, model$drift, model$diffusion, model$ddiffusion, model$prior_pdf, model$obs_pdf, model$theta, particles, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, - end_adaptive_phase, pmatch(mcmc_type, paste0("is", 1:3)), - threads, output_type) - } - } + end_adaptive_phase, pmatch(mcmc_type, paste0("is", 1:3)), + threads, output_type, verbose) + }) + colnames(out$alpha) <- model$state_names - - colnames(out$theta) <- rownames(out$S) <- colnames(out$S) <- names(model$theta) - + + colnames(out$theta) <- rownames(out$S) <- colnames(out$S) <- + names(model$theta) + out$iter <- iter out$burnin <- burnin out$thin <- thin @@ -708,10 +846,12 @@ run_mcmc.ssm_sde <- function(model, iter, particles, output_type = "full", out$output_type <- output_type out$call <- match.call() out$seed <- seed + dim(out$counts) <- NULL out$time <- proc.time() - a class(out) <- "mcmc_output" attr(out, "model_type") <- "ssm_sde" - attr(out, "ts") <- - list(start = start(model$y), end = end(model$y), frequency=frequency(model$y)) + attr(out, "ts") <- + list(start = start(model$y), end = end(model$y), + frequency = frequency(model$y)) out } diff --git a/R/sim_smoother.R b/R/sim_smoother.R index 7d53c136..6394a989 100644 --- a/R/sim_smoother.R +++ b/R/sim_smoother.R @@ -1,34 +1,51 @@ #' Simulation Smoothing #' -#' Function \code{sim_smoother} performs simulation smoothing i.e. simulates the states -#' from the conditional distribution \eqn{p(\alpha | y, \theta)} for linear-Gaussian models. +#' Function \code{sim_smoother} performs simulation smoothing i.e. simulates +#' the states from the conditional distribution \eqn{p(\alpha | y, \theta)} +#' for linear-Gaussian models. #' -#' For non-Gaussian/non-linear models, the simulation is based on the approximating -#' Gaussian model. +#' For non-Gaussian/non-linear models, the simulation is based on the +#' approximating Gaussian model. #' -#' @param model Model object. -#' @param nsim Number of independent samples. -#' @param use_antithetic Use an antithetic variable for location. -#' Default is \code{FALSE}. Ignored for multivariate models. -#' @param seed Seed for the random number generator. -#' @param ... Ignored. +#' @inheritParams importance_sample +#' @param model Model of class \code{bsm_lg}, \code{ar1_lg} +#' \code{ssm_ulg}, or \code{ssm_mlg}, or one of the non-gaussian models +#' \code{bsm_ng}, \code{ar1_ng} \code{svm}, +#' \code{ssm_ung}, or \code{ssm_mng}. #' @return An array containing the generated samples. #' @export #' @rdname sim_smoother #' @examples -#' model <- bsm_lg(rep(NA, 50), sd_level = uniform(1,0,5), sd_y = uniform(1,0,5)) -#' sim <- sim_smoother(model, 12) +#' # only missing data, simulates from prior +#' model <- bsm_lg(rep(NA, 25), sd_level = 1, +#' sd_y = 1) +#' # use antithetic variable for location +#' sim <- sim_smoother(model, nsim = 4, use_antithetic = TRUE, seed = 1) #' ts.plot(sim[, 1, ]) -sim_smoother <- function(model, nsim, seed, use_antithetic = FALSE, ...) { +#' cor(sim[, 1, ]) +sim_smoother <- function(model, nsim, seed, use_antithetic = TRUE, ...) { UseMethod("sim_smoother", model) } -#' @method sim_smoother gaussian +#' @method sim_smoother lineargaussian #' @rdname sim_smoother #' @export -sim_smoother.gaussian <- function(model, nsim = 1, - seed = sample(.Machine$integer.max, size = 1), use_antithetic = FALSE, ...) { - - out <- gaussian_sim_smoother(model, nsim, use_antithetic, seed, model_type(model)) +sim_smoother.lineargaussian <- function(model, nsim = 1, + seed = sample(.Machine$integer.max, size = 1), use_antithetic = TRUE, ...) { + + check_missingness(model) + + nsim <- check_intmax(nsim, "nsim") + seed <- check_intmax(seed, "seed", FALSE, max = .Machine$integer.max) + if (!test_flag(use_antithetic)) + stop("Argument 'use_antithetic' should be TRUE or FALSE. ") + nsamples <- ifelse(!is.null(nrow(model$y)), nrow(model$y), length(model$y)) * + length(model$a1) * nsim + if (nsim > 100 & nsamples > 1e10) { + warning(paste("Trying to sample ", nsamples, + "particles, you might run out of memory.")) + } + out <- gaussian_sim_smoother(model, nsim, use_antithetic, seed, + model_type(model)) rownames(out) <- names(model$a1) aperm(out, c(2, 1, 3))[-(length(model$y) + 1), , , drop = FALSE] } @@ -36,7 +53,8 @@ sim_smoother.gaussian <- function(model, nsim = 1, #' @rdname sim_smoother #' @export sim_smoother.nongaussian <- function(model, nsim = 1, - seed = sample(.Machine$integer.max, size = 1), use_antithetic = FALSE, ...) { + seed = sample(.Machine$integer.max, size = 1), use_antithetic = TRUE, ...) { + sim_smoother(gaussian_approx(model), nsim = nsim, use_antithetic = use_antithetic, seed = seed) } diff --git a/R/smoother.R b/R/smoother.R index 794dfb80..3770c5f7 100644 --- a/R/smoother.R +++ b/R/smoother.R @@ -4,9 +4,10 @@ #' computes only smoothed estimates of the states, and function #' \code{smoother} computes also smoothed variances. #' -#' For non-Gaussian models, the smoothing is based on the approximate Gaussian model. +#' For non-Gaussian models, the smoothing is based on the approximate Gaussian +#' model. #' -#' @param model Model model. +#' @inheritParams gaussian_approx #' @param ... Ignored. #' @return Matrix containing the smoothed estimates of states, or a list #' with the smoothed states and the variances. @@ -15,9 +16,18 @@ fast_smoother <- function(model, ...) { UseMethod("fast_smoother", model) } -#' @method fast_smoother gaussian +#' @method fast_smoother lineargaussian +#' @rdname smoother #' @export -fast_smoother.gaussian <- function(model, ...) { +#' @examples +#' model <- bsm_lg(Nile, +#' sd_level = tnormal(120, 100, 20, min = 0), +#' sd_y = tnormal(50, 50, 25, min = 0), +#' a1 = 1000, P1 = 200) +#' ts.plot(cbind(Nile, fast_smoother(model)), col = 1:2) +fast_smoother.lineargaussian <- function(model, ...) { + + check_missingness(model) out <- gaussian_fast_smoother(model, model_type(model)) colnames(out) <- names(model$a1) @@ -35,12 +45,25 @@ fast_smoother.nongaussian <- function(model, ...) { smoother <- function(model, ...) { UseMethod("smoother", model) } -#' @method smoother gaussian +#' @method smoother lineargaussian +#' @rdname smoother #' @export -smoother.gaussian <- function(model, ...) { +#' @examples +#' model <- bsm_lg(Nile, +#' sd_y = tnormal(120, 100, 20, min = 0), +#' sd_level = tnormal(50, 50, 25, min = 0), +#' a1 = 1000, P1 = 500^2) +#' +#' out <- smoother(model) +#' ts.plot(cbind(Nile, out$alphahat), col = 1:2) +#' ts.plot(sqrt(out$Vt[1, 1, ])) +smoother.lineargaussian <- function(model, ...) { + + check_missingness(model) out <- gaussian_smoother(model, model_type(model)) - colnames(out$alphahat) <- colnames(out$Vt) <- rownames(out$Vt) <- names(model$a1) + colnames(out$alphahat) <- colnames(out$Vt) <- rownames(out$Vt) <- + names(model$a1) out$Vt <- out$Vt[, , -nrow(out$alphahat), drop = FALSE] out$alphahat <- ts(out$alphahat[-nrow(out$alphahat), , drop = FALSE], @@ -57,21 +80,52 @@ smoother.nongaussian <- function(model, ...) { #' Extended Kalman Smoothing #' -#' Function \code{ekf_smoother} runs the (iterated) extended Kalman smoother for -#' the given non-linear Gaussian model of class \code{ssm_nlg}, -#' and returns the smoothed estimates of the states and the corresponding variances. +#' Function \code{ekf_smoother} runs the (iterated) extended Kalman smoother +#' for the given non-linear Gaussian model of class \code{ssm_nlg}, +#' and returns the smoothed estimates of the states and the corresponding +#' variances. Function \code{ekf_fast_smoother} computes only smoothed +#' estimates of the states. #' -#' @param model Model model -#' @param iekf_iter If \code{iekf_iter > 0}, iterated extended Kalman filter is -#' used with \code{iekf_iter} iterations. +#' @inheritParams ekf #' @return List containing the log-likelihood, -#' smoothed state estimates \code{alphahat}, and the corresponding variances \code{Vt} and -#' \code{Ptt}. +#' smoothed state estimates \code{alphahat}, and the corresponding variances +#' \code{Vt} and \code{Ptt}. #' @export #' @rdname ekf_smoother -#' @export +#' @examples +#' \donttest{ # Takes a while on CRAN +#' set.seed(1) +#' mu <- -0.2 +#' rho <- 0.7 +#' sigma_y <- 0.1 +#' sigma_x <- 1 +#' x <- numeric(50) +#' x[1] <- rnorm(1, mu, sigma_x / sqrt(1 - rho^2)) +#' for(i in 2:length(x)) { +#' x[i] <- rnorm(1, mu * (1 - rho) + rho * x[i - 1], sigma_x) +#' } +#' y <- rnorm(length(x), exp(x), sigma_y) +#' +#' pntrs <- cpp_example_model("nlg_ar_exp") +#' +#' model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, +#' Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, +#' Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, +#' theta = c(mu= mu, rho = rho, +#' log_sigma_x = log(sigma_x), log_sigma_y = log(sigma_y)), +#' log_prior_pdf = pntrs$log_prior_pdf, +#' n_states = 1, n_etas = 1, state_names = "state") +#' +#' out_ekf <- ekf_smoother(model_nlg, iekf_iter = 0) +#' out_iekf <- ekf_smoother(model_nlg, iekf_iter = 1) +#' ts.plot(cbind(x, out_ekf$alphahat, out_iekf$alphahat), col = 1:3) +#' } ekf_smoother <- function(model, iekf_iter = 0) { + check_missingness(model) + + iekf_iter <- check_intmax(iekf_iter, "iekf_iter", positive = FALSE) + out <- ekf_smoother_nlg(t(model$y), model$Z, model$H, model$T, model$R, model$Z_gn, model$T_gn, model$a1, model$P1, model$theta, model$log_prior_pdf, model$known_params, @@ -85,17 +139,20 @@ ekf_smoother <- function(model, iekf_iter = 0) { start = start(model$y), frequency = frequency(model$y)) out } - +#' @rdname ekf_smoother +#' @export ekf_fast_smoother <- function(model, iekf_iter = 0) { + check_missingness(model) + + iekf_iter <- check_intmax(iekf_iter, "iekf_iter", positive = FALSE) + out <- ekf_fast_smoother_nlg(t(model$y), model$Z, model$H, model$T, model$R, model$Z_gn, model$T_gn, model$a1, model$P1, model$theta, model$log_prior_pdf, model$known_params, model$known_tv_params, model$n_states, model$n_etas, as.integer(model$time_varying), iekf_iter) - colnames(out$alphahat) <- colnames(out$Vt) <- - rownames(out$Vt) <- model$state_names - ts(out[-nrow(out$alphahat), , drop = FALSE], start = start(model$y), + colnames(out$alphahat) <- model$state_names + ts(out$alphahat[-nrow(out$alphahat),, drop = FALSE], start = start(model$y), frequency = frequency(model$y)) } - diff --git a/R/srr-stats-standards.R b/R/srr-stats-standards.R new file mode 100644 index 00000000..7841c52e --- /dev/null +++ b/R/srr-stats-standards.R @@ -0,0 +1,44 @@ +#' srr_stats +#' +#' All of the following standards initially have `@srrstatsTODO` tags. +#' These may be moved at any time to any other locations in your code. +#' Once addressed, please modify the tag from `@srrstatsTODO` to `@srrstats`, +#' or `@srrstatsNA`, ensuring that references to every one of the following +#' standards remain somewhere within your code. +#' (These comments may be deleted at any time.) +#' +#' @srrstatsVerbose TRUE +#' @noRd +NULL +#' NA_standards +#' +#' Any non-applicable standards can have their tags changed from +#' `@srrstatsTODO` to `@srrstatsNA`, and placed together in this block, +#' along with explanations for why each of these standards have been deemed not +#' applicable. (These comments may also be deleted at any time.) +#' +#' @srrstatsNA {G2.4d, G2.4e, G2.5} Factor types are not used nor supported. +#' @srrstatsNA {G2.10, G2.11, G2.12, G2.13} No data.frame style tabular data is +#' used/supported as input +#' @srrstatsNA {G3.1, G3.1a} No sample covariance calculations done. +#' @srrstatsNA {G4.0} No output is written to local files. +#' @srrstatsNA {G5.3} Some functions can produce NAs and nonfinite values, +#' and there are some checks for these (e.g. in C++ side) but not explicitly +#' tested everywhere. +#' @srrstatsNA {G5.10, G5.11, G5.11a, G5.12} Package does not contain extended +#' tests (although benchmarks folder contains template for running such very +#' time-consuming tests), although some of the automatic tests are switched off +#' for CRAN due to the time limits. +#' @srrstatsNA {BS2.10} Not applicable as only single-chain runs are supported +#' (but several such runs can be combined with posterior package). +#' @srrstatsNA {BS2.11} Starting values are not accepted in this form. +#' @srrstatsNA {BS1.4, BS1.5, BS4.3, BS4.4, BS4.5, BS4.6, BS4.7, BS5.4} No +#' support for automatic stopping at converge (converge checkers). +#' @srrstatsNA {BS2.15} Errors are normal R errors so they can be caught? But +#' not sure what is meant here. +#' @srrstatsNA {BS3.1, BS3.2} Not really relevant for SSMs, or at least +#' difficult to check this kind of thing in general. +#' @srrstatsNA {BS6.1, BS6.2, BS6.3, BS6.5} Just suggests and illustrates using +#' ggplot or bayesplot packages, with several examples. +#' @noRd +NULL diff --git a/R/summary.R b/R/summary.R new file mode 100644 index 00000000..c8466821 --- /dev/null +++ b/R/summary.R @@ -0,0 +1,127 @@ +#' Summary Statistics of Posterior Samples +#' +#' This functions returns a data frame containing mean, standard deviations, +#' standard errors, and effective sample size estimates for parameters and +#' states. +#' +#' For IS-MCMC two types of standard errors are reported. +#' SE-IS can be regarded as the square root of independent IS variance, +#' whereas SE corresponds to the square root of total asymptotic variance +#' (see Remark 3 of Vihola et al. (2020)). +#' +#' @importFrom rlang .data +#' @param object Output from \code{run_mcmc} +#' @param variable Are the summary statistics computed for either +#' \code{"theta"} (default), \code{"states"}, or \code{"both"}? +#' @param return_se if \code{FALSE} (default), computation of standard +#' errors and effective sample sizes is omitted (as they can take considerable +#' time for models with large number of states and time points). +#' @param probs A numeric vector defining the quantiles of interest. Default is +#' \code{c(0.025, 0.975)}. +#' @param times A vector of indices. For states, for what time points the +#' summaries should be computed? Default is all, ignored if +#' \code{variable = "theta"}. +#' @param states A vector of indices. For what states the summaries should be +#' computed?. Default is all, ignored if +#' \code{variable = "theta"}. +#' @param method Method for computing integrated autocorrelation time. Default +#' is \code{"sokal"}, other option is \code{"geyer"}. +#' @param use_times If \code{TRUE} (default), transforms the values of the time +#' variable to match the ts attribute of the input to define. If \code{FALSE}, +#' time is based on the indexing starting from 1. +#' @param ... Ignored. +#' @return If \code{variable} is \code{"theta"} or \code{"states"}, a +#' \code{data.frame} object. If \code{"both"}, a list of two data frames. +#' @references +#' Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based +#' on approximate marginal Markov chain Monte Carlo. +#' Scand J Statist. 2020; 1-38. https://doi.org/10.1111/sjos.12492 +#' @export +#' @srrstats {BS5.3, BS5.5, BS6.4} +#' @examples +#' data("negbin_model") +#' summary(negbin_model, return_se = TRUE, method = "geyer") +#' summary(negbin_model, times = c(1, 200), prob = c(0.05, 0.5, 0.95)) +summary.mcmc_output <- function(object, return_se = FALSE, variable = "theta", + probs = c(0.025, 0.975), times, states, use_times = TRUE, method = "sokal", + ...) { + + if (!test_flag(return_se)) + stop("Argument 'return_se' should be TRUE or FALSE. ") + + method <- match.arg(method, c("sokal", "geyer")) + + variable <- match.arg(tolower(variable), c("theta", "states", "both")) + + if (return_se) { + if (object$mcmc_type %in% paste0("is", 1:3)) { + summary_f <- function(x, w) { + c(Mean = weighted_mean(x, w), + SE = sqrt(asymptotic_var(x, w, method)), + SD = sqrt(weighted_var(x, w)), + weighted_quantile(x, w, probs), + ESS = round(estimate_ess(x, w, method)), + SE_IS = weighted_se(x, w), + ESS_IS = round(ess(w, identity, x))) + } + } else { + summary_f <- function(x, w) { + c(Mean = mean(x), SE = sqrt(asymptotic_var(x, method = method)), + SD = sd(x), quantile(x, probs), + ESS = round(estimate_ess(x, method = method))) + } + } + } else { + if (object$mcmc_type %in% paste0("is", 1:3)) { + summary_f <- function(x, w) { + c(Mean = weighted_mean(x, w), + SD = sqrt(weighted_var(x, w)), + weighted_quantile(x, w, probs)) + } + } else { + summary_f <- function(x, w) { + c(Mean = mean(x), + SD = sd(x), quantile(x, probs)) + } + } + } + if (variable %in% c("theta", "both")) { + sumr_theta <- + as.data.frame(object, variable = "theta", expand = TRUE) |> + group_by(.data$variable) |> + summarise(as_tibble(as.list(summary_f(.data$value, .data$weight)))) |> + as.data.frame() + if (variable == "theta") return(sumr_theta) + } + + if (variable %in% c("states", "both")) { + if (object$output_type != 1) + stop("Cannot return summary of states as the MCMC type was not 'full'. ") + + if (missing(times)) { + times <- seq_len(nrow(object$alpha)) + } else { + if (!test_integerish(times, lower = 1, upper = nrow(object$alpha), + any.missing = FALSE, unique = TRUE)) + stop(paste0("Argument 'times' should contain indices between 1 and ", + nrow(object$alpha),".")) + } + if (missing(states)) { + states <- seq_len(ncol(object$alpha)) + } else { + if (!test_integerish(states, lower = 1, upper = ncol(object$alpha), + any.missing = FALSE, unique = TRUE)) + stop(paste0("Argument 'states' should contain indices between 1 and ", + ncol(object$alpha),".")) + } + + sumr_states <- + as.data.frame(object, variable = "states", expand = TRUE, + times = times, states = states, use_times = use_times) |> + group_by(.data$variable, .data$time) |> + summarise(as_tibble(as.list(summary_f(.data$value, .data$weight)))) |> + as.data.frame() + if (variable == "states") return(sumr_states) + } + list(theta = sumr_theta, states = sumr_states) +} diff --git a/R/zzz.R b/R/zzz.R index 12d0d826..6762d661 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,3 +1,3 @@ -.onUnload <- function (libpath) { +.onUnload <- function(libpath) { library.dynam.unload("bssm", libpath) } diff --git a/README.Rmd b/README.Rmd new file mode 100644 index 00000000..9a31b803 --- /dev/null +++ b/README.Rmd @@ -0,0 +1,280 @@ +--- +output: github_document +--- + + + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + fig.path = "man/figures/README-", + out.width = "100%", + cache = TRUE +) +``` + +```{r srr-tags, eval = FALSE, echo = FALSE} +#' @srrstats {G1.2} Contains project status badge. +#' @srrstats {G1.4,G1.4a} Package uses roxygen2 for documentation. +#' @srrstats {G2.0, G2.0a, G2.1, G2.1a, G2.2, G2.4, G2.4a, G2.4b, G2.4c, G2.6} +#' Input types and shapes are tested and checked with autotest and converted +#' explicitly when necessary. +#' +#' @srrstats {G2.3, G2.3a, G2.3b} match.arg and tolower are used where +#' applicable. +#' @srrstats {G1.0, G1.3, G1.4, G1.4a, G1.5, G1.6} General +#' documentation, addressed by the vignettes and the corresponding R +#' Journal paper. +#' @srrstats {G1.1} This is the first software to implement the IS-MCMC by +#' Vihola, Helske, and Franks (2020) and first R package to implement delayed +#' acceptance pseudo-marginal MCMC for state space models. The IS-MCMC method +#' is also available in [walker](github.com/helske/walker) package for a +#' limited class of time-varying GLMss (a small subset of the models +#' supported by this package). Some of the functionality for exponential family +#' state space models is also available in [KFAS](github.com/helske/KFAS), and +#' those models can be converted easily to bssm format for Bayesian analysis. +#' @srrstats {G2.4, G2.4a, G2.4b, G2.4c, G2.6} Explicit conversions are used +#' where necessary. +#' +#' @srrstats {G2.14, G2.14a, G2.14b, G2.14c, G2.15, G2.16} Missing observations +#' (y) are handled automatically as per SSM theory, whereas missing values are +#' not allowed elsewhere. Inputing or ignoring them does not make sense in time +#' series context. +#' +#' @srrstats {G3.0} No floating point equality comparisons are made. +#' +#' @srrstats {G5.4, G5.4a, G5.4b, G5.4c, G5.5, G5.6, G5.6a, G5.6b, G5.7} and +#' @srrstats {BS4.0, BS4.1} The algorithms work as defined per Vihola, Helske, +#' Franks (2020) (all simulations were implemented with the bssm package) and +#' Helske and Vihola (2021). Full replication of the results would take +#' days/weeks (but see also bsm_ng, negbin_series and several testthat tests). +#' +#' @srrstats {G5.8, G5.8a, G5.8b, G5.8c, G5.8d} Tested with autotest and the +#' testthat tests. +#' @srrstats {G5.9, G5.9a, G5.9b} Tested with autotest and the testthat tests. +#' +#' @srrstats {BS1.0, BS1.1, BS1.2, BS1.2a, BS1.2b, BS1.3b} Addressed in the +#' models.R, run_mcmc.R, in vignettes and in the R Journal paper. +#' +#' @srrstats {BS2.1, BS2.1a, BS2.6} Tested and demonstrated by autotest and +#' package examples/tests. +#' @srrstats {BS7.4, BS7.4a} The scales do not matter (in terms of runtime) +#' in random walk Metropolis nor in particle filters, as long as numerical +#' issues are not encountered +``` + +# bssm + + +[![Project Status: Active - The project has reached a stable, usable state and is being actively developed](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) +[![Status at rOpenSci Software Peer Review](https://badges.ropensci.org/489_status.svg)](https://github.com/ropensci/software-review/issues/489) +[![R-CMD-check](https://github.com/helske/bssm/workflows/R-CMD-check/badge.svg)](https://github.com/helske/bssm/actions) +[![Codecov test coverage](https://codecov.io/gh/helske/bssm/graph/badge.svg)](https://app.codecov.io/gh/helske/bssm) +[![CRAN version](http://www.r-pkg.org/badges/version/bssm)]( https://CRAN.R-project.org/package=bssm) +[![downloads](https://cranlogs.r-pkg.org/badges/bssm)](https://cranlogs.r-pkg.org/badges/bssm) + + + +The `bssm` R package provides efficient methods for Bayesian inference of state +space models via particle Markov chain Monte Carlo and importance sampling type +weighted MCMC. +Currently Gaussian, Poisson, binomial, negative binomial, and Gamma observation +densities with linear-Gaussian state dynamics, as well as general non-linear +Gaussian models and discretely observed latent diffusion processes are +supported. + +For details, see + +* [The bssm paper on The R Journal](https://journal.r-project.org/archive/2021/RJ-2021-103/index.html), +* [Package vignettes at CRAN](https://CRAN.R-project.org/package=bssm) +* Paper on [Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo](https://onlinelibrary.wiley.com/doi/abs/10.1111/sjos.12492) + +There are also couple posters and a talk related to IS-correction methodology and bssm package: + +* [UseR!2021 talk slides](https://jounihelske.netlify.app/talk/user2021/) +* [SMC 2017 workshop: Accelerating MCMC with an approximation ](http://users.jyu.fi/~jovetale/posters/SMC2017) +* [UseR!2017: Bayesian non-Gaussian state space models in R](http://users.jyu.fi/~jovetale/posters/user2017.pdf) + +The `bssm` package was originally developed with the support of Academy of Finland grants 284513, 312605, 311877, and 331817. Current development is focused on increased usability. For recent changes, see NEWS file. + +### Citing the package + +If you use the `bssm` package in publications, please cite the corresponding R Journal paper: + +Jouni Helske and Matti Vihola (2021). "bssm: Bayesian Inference of Non-linear and Non-Gaussian State Space Models in R." The R Journal (2021) 13:2, pages 578-589. https://journal.r-project.org/archive/2021/RJ-2021-103/index.html + +## Installation + +You can install the released version of bssm from [CRAN](https://CRAN.R-project.org) with: + +```{r, eval=FALSE} +install.packages("bssm") +``` + +And the development version from [GitHub](https://github.com/) with: + +```{r, eval=FALSE} +# install.packages("devtools") +devtools::install_github("helske/bssm") +``` +Or from R-universe with + +```{r, eval = FALSE} +install.packages("bssm", repos = "https://helske.r-universe.dev") +``` + +## Example + +Consider the daily air quality measurements in New Your from May to September 1973, available in the `datasets` package. Let's try to predict the missing ozone levels by simple linear-Gaussian local linear trend model with temperature and wind as explanatory variables (missing response variables are handled naturally in the state space modelling framework, however no missing values in covariates are normally allowed); + +```{r example} +library("bssm") +library("dplyr") +library("ggplot2") +set.seed(1) + +data("airquality", package = "datasets") + +# Covariates as matrix. For complex cases, check out as_bssm function +xreg <- airquality |> select(Wind, Temp) |> as.matrix() + +model <- bsm_lg(airquality$Ozone, + xreg = xreg, + # Define priors for hyperparameters (i.e. not the states), see ?bssm_prior + # Initial value followed by parameters of the prior distribution + beta = normal_prior(rep(0, ncol(xreg)), 0, 1), + sd_y = gamma_prior(1, 2, 0.01), + sd_level = gamma_prior(1, 2, 0.01), + sd_slope = gamma_prior(1, 2, 0.01)) + +fit <- run_mcmc(model, iter = 20000, burnin = 5000) +fit + +obs <- data.frame(Time = 1:nrow(airquality), + Ozone = airquality$Ozone) |> filter(!is.na(Ozone)) + +pred <- fitted(fit, model) +pred |> + ggplot(aes(x = Time, y = Mean)) + + geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`), + alpha = 0.5, fill = "steelblue") + + geom_line() + + geom_point(data = obs, + aes(x = Time, y = Ozone), colour = "Tomato") + + theme_bw() + +``` + +Same model but now assuming observations are from Gamma distribution: +```{r gamma-example} + +model2 <- bsm_ng(airquality$Ozone, + xreg = xreg, + beta = normal(rep(0, ncol(xreg)), 0, 1), + distribution = "gamma", + phi = gamma_prior(1, 2, 0.01), + sd_level = gamma_prior(1, 2, 0.1), + sd_slope = gamma_prior(1, 2, 0.1)) + +fit2 <- run_mcmc(model2, iter = 20000, burnin = 5000, particles = 10) +fit2 +``` + +Comparison: +```{r compare} +pred2 <- fitted(fit2, model2) + +bind_rows(list(Gaussian = pred, Gamma = pred2), .id = "Model") |> + ggplot(aes(x = Time, y = Mean)) + + geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`, fill = Model), + alpha = 0.25) + + geom_line(aes(colour = Model)) + + geom_point(data = obs, + aes(x = Time, y = Ozone)) + + theme_bw() +``` + + +Now let's assume that we also want to use the solar radiation variable as predictor for ozone. As it contains few missing values, we cannot use it directly. As the number of missing time points is very small, simple imputation would likely be acceptable, but let's consider more another approach. For simplicity, the slope terms of the previous models are now omitted, and we focus on the Gaussian case. Let $\mu_t$ be the true solar radiation at time $t$. Now for ozone $O_t$ we assume following model: + +$O_t = D_t + \alpha_t + \beta_S \mu_t + \sigma_\epsilon \epsilon_t$\ +$\alpha_{t+1} = \alpha_t + \sigma_\eta\eta_t$\ +$\alpha_1 \sim N(0, 100^2\textrm{I})$,\ +wheere $D_t = \beta X_t$ contains regression terms related to wind and temperature, $\alpha_t$ is the time varying intercept term, and $\beta_S$ is the effect of solar radiation $\mu_t$. + +Now for the observed solar radiation $S_t$ we assume + +$S_t = \mu_t$\ +$\mu_{t+1} = \mu_t + \sigma_\xi\xi_t,$\ +$\mu_1 \sim N(0, 100^2)$,\ +i.e. we assume as simple random walk for the $\mu$ which we observe without error or not at all (there is no error term in the observation equation $S_t=\mu_t$). + +We combine these two models as a bivariate Gaussian model with `ssm_mlg`: + +```{r missing-values} +# predictors (not including solar radiation) for ozone +xreg <- airquality |> select(Wind, Temp) |> as.matrix() + +# Function which outputs new model components given the parameter vector theta +update_fn <- function(theta) { + D <- rbind(t(xreg %*% theta[1:2]), 1) + Z <- matrix(c(1, 0, theta[3], 1), 2, 2) + R <- diag(exp(theta[4:5])) + H <- diag(c(exp(theta[6]), 0)) + # add third dimension so we have p x n x 1, p x m x 1, p x p x 1 arrays + dim(Z)[3] <- dim(R)[3] <- dim(H)[3] <- 1 + list(D = D, Z = Z, R = R, H = H) +} + +# Function for log-prior density +prior_fn <- function(theta) { + sum(dnorm(theta[1:3], 0, 10, log = TRUE)) + + sum(dgamma(exp(theta[4:6]), 2, 0.01, log = TRUE)) + + sum(theta[4:6]) # log-jacobian +} + +init_theta <- c(0, 0, 0, log(50), log(5), log(20)) +comps <- update_fn(init_theta) + +model <- ssm_mlg(y = cbind(Ozone = airquality$Ozone, Solar = airquality$Solar.R), + Z = comps$Z, D = comps$D, H = comps$H, T = diag(2), R = comps$R, + a1 = rep(0, 2), P1 = diag(100, 2), init_theta = init_theta, + state_names = c("alpha", "mu"), update_fn = update_fn, + prior_fn = prior_fn) + +fit <- run_mcmc(model, iter = 60000, burnin = 10000) +fit +``` + +Draw predictions: +```{r bivariate-fig} +pred <- fitted(fit, model) + +obs <- data.frame(Time = 1:nrow(airquality), + Solar = airquality$Solar.R) |> filter(!is.na(Solar)) + +pred |> filter(Variable == "Solar") |> + ggplot(aes(x = Time, y = Mean)) + + geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`), + alpha = 0.5, fill = "steelblue") + + geom_line() + + geom_point(data = obs, + aes(x = Time, y = Solar), colour = "Tomato") + + theme_bw() + + +obs <- data.frame(Time = 1:nrow(airquality), + Ozone = airquality$Ozone) |> filter(!is.na(Ozone)) + +pred |> filter(Variable == "Ozone") |> + ggplot(aes(x = Time, y = Mean)) + + geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`), + alpha = 0.5, fill = "steelblue") + + geom_line() + + geom_point(data = obs, + aes(x = Time, y = Ozone), colour = "Tomato") + + theme_bw() +``` + +See more examples in the paper, vignettes, and in the docs. diff --git a/README.md b/README.md index bf3047d8..3416f0ef 100644 --- a/README.md +++ b/README.md @@ -1,111 +1,378 @@ - [![R-CMD-check](https://github.com/helske/bssm/workflows/R-CMD-check/badge.svg)](https://github.com/helske/bssm/actions) -[![cran version](http://www.r-pkg.org/badges/version/bssm)](http://cran.r-project.org/package=bssm) -[![downloads](http://cranlogs.r-pkg.org/badges/bssm)](http://cranlogs.r-pkg.org/badges/bssm) - - - -bssm: an R package for Bayesian inference of state space models -========================================================================== - -Efficient methods for Bayesian inference of state space models via particle Markov -chain Monte Carlo and importance sampling type weighted Markov chain Monte Carlo. -Currently Gaussian, Poisson, binomial, negative binomial, and Gamma observation densities -and linear-Gaussian state dynamics, as well as general non-linear Gaussian models and discretely observed latent diffusion processes are supported. - -For details, see [paper on ArXiv](https://arxiv.org/abs/2101.08492), [package vignettes at CRAN](https://cran.r-project.org/web/packages/bssm/index.html) and paper on [Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo](https://onlinelibrary.wiley.com/doi/abs/10.1111/sjos.12492). There are also couple posters related to IS-correction methodology: [SMC 2017 workshop: Accelerating MCMC with an approximation ](http://users.jyu.fi/~jovetale/posters/SMC2017) and [UseR!2017: Bayesian non-Gaussian state space models in R](http://users.jyu.fi/~jovetale/posters/user2017.pdf). - - -You can install the latest development version by using the devtools package: - -```R -install.packages("devtools") -devtools::install_github("helske/bssm") -``` - -Recent changes (For all changes, see NEWS file.) -========================================================================== - -bssm 1.1.3-2 (Release date: 2021-02-23) -============== - * Fixed missing parenthesis causing compilation fail in case of no OpenMP support. - * Added pandoc version >= 1.12.3 to system requirements. - -bssm 1.1.3-1 (Release date: 2021-02-22) -============== - * Fixed PM-MCMC and DA-MCMC for SDE models and added an example to `ssm_sde`. - * Added vignette for SDE models. - * Updated citation information and streamlined the main vignette. - -bssm 1.1.2 (Release date: 2021-02-08) -============== - * Some bug fixes, see NEWS for details. - -bssm 1.1.0 (Release date: 2021-01-19) -============== - - * Added function `suggest_N` which can be used to choose - suitable number of particles for IS-MCMC. - * Added function `post_correct` which can be used to update - previous approximate MCMC with IS-weights. - * Gamma priors are now supported in easy-to-use models such as `bsm_lg`. - * The adaptation of the proposal distribution now continues also after the burn-in by default. - * Changed default MCMC type to typically most efficient and robust IS2. - * Renamed `nsim` argument to `particles` in most of the R functions (`nsim` also works with a warning). - * Fixed a bug with bsm models with covariates, where all standard deviation parameters were fixed. - This resulted error within MCMC algorithms. - * Fixed a dimension drop bug in the predict method which caused error for univariate models. - * Fixed few typos in vignette (thanks Kyle Hussman) and added more examples. - -bssm 1.0.1-1 (Release date: 2020-11-12) -============== - - * Added an argument `future` for predict method which allows - predictions for current time points by supplying the original model - (e.g., for posterior predictive checks). - At the same time the argument name `future_model` was changed to `model`. - * Fixed a bug in summary.mcmc_run which resulted error when - trying to obtain summary for states only. - * Added a check for Kalman filter for a degenerate case where all - observational level and state level variances are zero. - * Renamed argument `n_threads` to `threads` for consistency - with `iter` and `burnin` arguments. - * Improved documentation, added examples. - * Added a vignette regarding psi-APF for non-linear models. - -bssm 1.0.0 (Release date: 2020-06-09) -============== -Major update - - * Major changes for model definitions, now model updating and priors - can be defined via R functions (non-linear and SDE models still rely on C++ snippets). - * Added support for multivariate non-Gaussian models. - * Added support for gamma distributions. - * Added the function as.data.frame for mcmc output which converts the MCMC samples - to data.frame format for easier post-processing. - * Added truncated normal prior. - * Many argument names and model building functions have been changed for clarity and consistency. - * Major overhaul of C++ internals which can bring minor efficiency gains and smaller installation size. - * Allow zero as initial value for positive-constrained parameters of bsm models. - * Small changes to summary method which can now return also only summaries of the states. - * Fixed a bug in initializing run_mcmc for negative binomial model. - * Fixed a bug in phi-APF for non-linear models. - * Reimplemented predict method which now always produces data frame of samples. - -bssm 0.1.11 (Release date: 2020-02-25) -============== - * Switched (back) to approximate posterior in RAM for PM-SPDK and PM-PSI, - as it seems to work better with noisy likelihood estimates. - * Print and summary methods for MCMC output are now coherent in their output. - -bssm 0.1.10 (Release date: 2020-02-04) -============== - * Fixed missing weight update for IS-SPDK without OPENMP flag. - * Removed unused usage argument ... from expand_sample. - -bssm 0.1.9 (Release date: 2020-01-27) -============== - * Fixed state sampling for PM-MCMC with SPDK. - * Added ts attribute for svm model. - * Corrected asymptotic variance for summary methods. - -For older versions, see NEWS file. + + + +# bssm + + + +[![Project Status: Active - The project has reached a stable, usable +state and is being actively +developed](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) +[![Status at rOpenSci Software Peer +Review](https://badges.ropensci.org/489_status.svg)](https://github.com/ropensci/software-review/issues/489) +[![R-CMD-check](https://github.com/helske/bssm/workflows/R-CMD-check/badge.svg)](https://github.com/helske/bssm/actions) +[![Codecov test +coverage](https://codecov.io/gh/helske/bssm/graph/badge.svg)](https://app.codecov.io/gh/helske/bssm) +[![CRAN +version](http://www.r-pkg.org/badges/version/bssm)](https://CRAN.R-project.org/package=bssm) +[![downloads](https://cranlogs.r-pkg.org/badges/bssm)](https://cranlogs.r-pkg.org/badges/bssm) + + + +The `bssm` R package provides efficient methods for Bayesian inference +of state space models via particle Markov chain Monte Carlo and +importance sampling type weighted MCMC. Currently Gaussian, Poisson, +binomial, negative binomial, and Gamma observation densities with +linear-Gaussian state dynamics, as well as general non-linear Gaussian +models and discretely observed latent diffusion processes are supported. + +For details, see + +- [The bssm paper on The R + Journal](https://journal.r-project.org/archive/2021/RJ-2021-103/index.html), +- [Package vignettes at CRAN](https://CRAN.R-project.org/package=bssm) +- Paper on [Importance sampling type estimators based on approximate + marginal Markov chain Monte + Carlo](https://onlinelibrary.wiley.com/doi/abs/10.1111/sjos.12492) + +There are also couple posters and a talk related to IS-correction +methodology and bssm package: + +- [UseR!2021 talk + slides](https://jounihelske.netlify.app/talk/user2021/) +- [SMC 2017 workshop: Accelerating MCMC with an + approximation](http://users.jyu.fi/~jovetale/posters/SMC2017) +- [UseR!2017: Bayesian non-Gaussian state space models in + R](http://users.jyu.fi/~jovetale/posters/user2017.pdf) + +The `bssm` package was originally developed with the support of Academy +of Finland grants 284513, 312605, 311877, and 331817. Current +development is focused on increased usability. For recent changes, see +NEWS file. + +### Citing the package + +If you use the `bssm` package in publications, please cite the +corresponding R Journal paper: + +Jouni Helske and Matti Vihola (2021). “bssm: Bayesian Inference of +Non-linear and Non-Gaussian State Space Models in R.” The R Journal +(2021) 13:2, pages 578-589. + + +## Installation + +You can install the released version of bssm from +[CRAN](https://CRAN.R-project.org) with: + +``` r +install.packages("bssm") +``` + +And the development version from [GitHub](https://github.com/) with: + +``` r +# install.packages("devtools") +devtools::install_github("helske/bssm") +``` + +Or from R-universe with + +``` r +install.packages("bssm", repos = "https://helske.r-universe.dev") +``` + +## Example + +Consider the daily air quality measurements in New Your from May to +September 1973, available in the `datasets` package. Let’s try to +predict the missing ozone levels by simple linear-Gaussian local linear +trend model with temperature and wind as explanatory variables (missing +response variables are handled naturally in the state space modelling +framework, however no missing values in covariates are normally +allowed); + +``` r +library("bssm") +#> Warning: package 'bssm' was built under R version 4.3.1 +#> +#> Attaching package: 'bssm' +#> The following object is masked from 'package:base': +#> +#> gamma +library("dplyr") +#> +#> Attaching package: 'dplyr' +#> The following objects are masked from 'package:stats': +#> +#> filter, lag +#> The following objects are masked from 'package:base': +#> +#> intersect, setdiff, setequal, union +library("ggplot2") +#> Warning: package 'ggplot2' was built under R version 4.3.1 +set.seed(1) + +data("airquality", package = "datasets") + +# Covariates as matrix. For complex cases, check out as_bssm function +xreg <- airquality |> select(Wind, Temp) |> as.matrix() + +model <- bsm_lg(airquality$Ozone, + xreg = xreg, + # Define priors for hyperparameters (i.e. not the states), see ?bssm_prior + # Initial value followed by parameters of the prior distribution + beta = normal_prior(rep(0, ncol(xreg)), 0, 1), + sd_y = gamma_prior(1, 2, 0.01), + sd_level = gamma_prior(1, 2, 0.01), + sd_slope = gamma_prior(1, 2, 0.01)) + +fit <- run_mcmc(model, iter = 20000, burnin = 5000) +fit +#> +#> Call: +#> run_mcmc.lineargaussian(model = model, iter = 20000, burnin = 5000) +#> +#> Iterations = 5001:20000 +#> Thinning interval = 1 +#> Length of the final jump chain = 3593 +#> +#> Acceptance rate after the burn-in period: 0.239 +#> +#> Summary for theta: +#> +#> variable Mean SE SD 2.5% 97.5% ESS +#> Temp 1.0265846 0.007497538 0.2064343 0.60226671 1.400436 758 +#> Wind -2.5183269 0.020978488 0.5764833 -3.68987992 -1.327578 755 +#> sd_level 6.3731836 0.113153715 2.8013937 1.52958636 12.403961 613 +#> sd_slope 0.3388712 0.010355574 0.2833955 0.04210885 1.070284 749 +#> sd_y 20.8618647 0.068145131 1.9369381 17.08728231 24.722309 808 +#> +#> Summary for alpha_154: +#> +#> variable time Mean SE SD 2.5% 97.5% ESS +#> level 154 -28.3163054 0.69650977 20.132341 -69.271049 11.797133 835 +#> slope 154 -0.3740463 0.03683278 1.685733 -4.065499 2.830134 2094 +#> +#> Run time: +#> user system elapsed +#> 0.57 0.02 0.63 + +obs <- data.frame(Time = 1:nrow(airquality), + Ozone = airquality$Ozone) |> filter(!is.na(Ozone)) + +pred <- fitted(fit, model) +pred |> + ggplot(aes(x = Time, y = Mean)) + + geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`), + alpha = 0.5, fill = "steelblue") + + geom_line() + + geom_point(data = obs, + aes(x = Time, y = Ozone), colour = "Tomato") + + theme_bw() +``` + + + +Same model but now assuming observations are from Gamma distribution: + +``` r + +model2 <- bsm_ng(airquality$Ozone, + xreg = xreg, + beta = normal(rep(0, ncol(xreg)), 0, 1), + distribution = "gamma", + phi = gamma_prior(1, 2, 0.01), + sd_level = gamma_prior(1, 2, 0.1), + sd_slope = gamma_prior(1, 2, 0.1)) + +fit2 <- run_mcmc(model2, iter = 20000, burnin = 5000, particles = 10) +fit2 +#> +#> Call: +#> run_mcmc.nongaussian(model = model2, iter = 20000, particles = 10, +#> burnin = 5000) +#> +#> Iterations = 5001:20000 +#> Thinning interval = 1 +#> Length of the final jump chain = 3858 +#> +#> Acceptance rate after the burn-in period: 0.257 +#> +#> Summary for theta: +#> +#> variable Mean SE SD 2.5% 97.5% ESS +#> Temp 0.052808820 0.0002404538 0.008701489 0.0353736458 0.06992423 1310 +#> Wind -0.057351094 0.0004338213 0.015411504 -0.0873384757 -0.02700112 1262 +#> phi 4.006977632 0.0159088062 0.536273508 3.0263444882 5.15527365 1136 +#> sd_level 0.057158663 0.0020138200 0.035366227 0.0083794202 0.14651419 308 +#> sd_slope 0.003894013 0.0001752319 0.003654978 0.0004250207 0.01374575 435 +#> SE_IS ESS_IS +#> 7.128104e-05 14485 +#> 1.263047e-04 13905 +#> 4.411840e-03 14611 +#> 2.927386e-04 10591 +#> 3.031489e-05 7766 +#> +#> Summary for alpha_154: +#> +#> variable time Mean SE SD 2.5% 97.5% ESS +#> level 154 -0.200656509 0.0201721601 0.73134471 -1.62501396 1.24522802 1314 +#> slope 154 -0.002689176 0.0005121944 0.02289051 -0.04650504 0.04724173 1997 +#> SE_IS ESS_IS +#> 0.005987284 9458 +#> 0.000191620 6448 +#> +#> Run time: +#> user system elapsed +#> 7.50 0.01 7.71 +``` + +Comparison: + +``` r +pred2 <- fitted(fit2, model2) + +bind_rows(list(Gaussian = pred, Gamma = pred2), .id = "Model") |> + ggplot(aes(x = Time, y = Mean)) + + geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`, fill = Model), + alpha = 0.25) + + geom_line(aes(colour = Model)) + + geom_point(data = obs, + aes(x = Time, y = Ozone)) + + theme_bw() +``` + + + +Now let’s assume that we also want to use the solar radiation variable +as predictor for ozone. As it contains few missing values, we cannot use +it directly. As the number of missing time points is very small, simple +imputation would likely be acceptable, but let’s consider more another +approach. For simplicity, the slope terms of the previous models are now +omitted, and we focus on the Gaussian case. Let $\mu_t$ be the true +solar radiation at time $t$. Now for ozone $O_t$ we assume following +model: + +$O_t = D_t + \alpha_t + \beta_S \mu_t + \sigma_\epsilon \epsilon_t$ +$\alpha_{t+1} = \alpha_t + \sigma_\eta\eta_t$ +$\alpha_1 \sim N(0, 100^2\textrm{I})$, +wheere $D_t = \beta X_t$ contains regression terms related to wind and +temperature, $\alpha_t$ is the time varying intercept term, and +$\beta_S$ is the effect of solar radiation $\mu_t$. + +Now for the observed solar radiation $S_t$ we assume + +$S_t = \mu_t$ +$\mu_{t+1} = \mu_t + \sigma_\xi\xi_t,$ +$\mu_1 \sim N(0, 100^2)$, +i.e. we assume as simple random walk for the $\mu$ which we observe +without error or not at all (there is no error term in the observation +equation $S_t=\mu_t$). + +We combine these two models as a bivariate Gaussian model with +`ssm_mlg`: + +``` r +# predictors (not including solar radiation) for ozone +xreg <- airquality |> select(Wind, Temp) |> as.matrix() + +# Function which outputs new model components given the parameter vector theta +update_fn <- function(theta) { + D <- rbind(t(xreg %*% theta[1:2]), 1) + Z <- matrix(c(1, 0, theta[3], 1), 2, 2) + R <- diag(exp(theta[4:5])) + H <- diag(c(exp(theta[6]), 0)) + # add third dimension so we have p x n x 1, p x m x 1, p x p x 1 arrays + dim(Z)[3] <- dim(R)[3] <- dim(H)[3] <- 1 + list(D = D, Z = Z, R = R, H = H) +} + +# Function for log-prior density +prior_fn <- function(theta) { + sum(dnorm(theta[1:3], 0, 10, log = TRUE)) + + sum(dgamma(exp(theta[4:6]), 2, 0.01, log = TRUE)) + + sum(theta[4:6]) # log-jacobian +} + +init_theta <- c(0, 0, 0, log(50), log(5), log(20)) +comps <- update_fn(init_theta) + +model <- ssm_mlg(y = cbind(Ozone = airquality$Ozone, Solar = airquality$Solar.R), + Z = comps$Z, D = comps$D, H = comps$H, T = diag(2), R = comps$R, + a1 = rep(0, 2), P1 = diag(100, 2), init_theta = init_theta, + state_names = c("alpha", "mu"), update_fn = update_fn, + prior_fn = prior_fn) + +fit <- run_mcmc(model, iter = 60000, burnin = 10000) +fit +#> +#> Call: +#> run_mcmc.lineargaussian(model = model, iter = 60000, burnin = 10000) +#> +#> Iterations = 10001:60000 +#> Thinning interval = 1 +#> Length of the final jump chain = 12234 +#> +#> Acceptance rate after the burn-in period: 0.245 +#> +#> Summary for theta: +#> +#> variable Mean SE SD 2.5% 97.5% ESS +#> theta_1 -3.89121114 0.0233827004 0.58715113 -5.0085134 -2.6915137 631 +#> theta_2 0.98712126 0.0051506907 0.18819758 0.5917823 1.3475147 1335 +#> theta_3 0.06324657 0.0004672314 0.02417334 0.0141425 0.1100184 2677 +#> theta_4 0.82577262 0.0165661049 0.67134723 -0.6840637 1.9160168 1642 +#> theta_5 4.75567621 0.0010887250 0.05858454 4.6446809 4.8704036 2895 +#> theta_6 3.05462451 0.0014803971 0.07640392 2.9032635 3.2028023 2664 +#> +#> Summary for alpha_154: +#> +#> variable time Mean SE SD 2.5% 97.5% ESS +#> alpha 154 -16.44435 0.3659912 14.99708 -46.321645 13.01863 1679 +#> mu 154 223.60490 1.3409568 116.49063 -6.206301 453.18554 7546 +#> +#> Run time: +#> user system elapsed +#> 7.41 0.11 7.83 +``` + +Draw predictions: + +``` r +pred <- fitted(fit, model) + +obs <- data.frame(Time = 1:nrow(airquality), + Solar = airquality$Solar.R) |> filter(!is.na(Solar)) + +pred |> filter(Variable == "Solar") |> + ggplot(aes(x = Time, y = Mean)) + + geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`), + alpha = 0.5, fill = "steelblue") + + geom_line() + + geom_point(data = obs, + aes(x = Time, y = Solar), colour = "Tomato") + + theme_bw() +``` + + + +``` r + + +obs <- data.frame(Time = 1:nrow(airquality), + Ozone = airquality$Ozone) |> filter(!is.na(Ozone)) + +pred |> filter(Variable == "Ozone") |> + ggplot(aes(x = Time, y = Mean)) + + geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`), + alpha = 0.5, fill = "steelblue") + + geom_line() + + geom_point(data = obs, + aes(x = Time, y = Ozone), colour = "Tomato") + + theme_bw() +``` + + + +See more examples in the paper, vignettes, and in the docs. diff --git a/benchmarks/replications.Rmd b/benchmarks/replications.Rmd new file mode 100644 index 00000000..3d785225 --- /dev/null +++ b/benchmarks/replications.Rmd @@ -0,0 +1,86 @@ +--- +title: "Replications" +author: "Jouni Helske" +date: "11/17/2021" +output: html_document +--- + +```{r srr-tags, eval = FALSE, echo = FALSE} +#' @srrstats {G5.4, G5.4a, G5.4b, G5.4c, G5.5, G5.6, G5.6a, G5.6b, G5.7} The +#' algorithms work correctly as per Vihola, Helske, Franks (2020) +#' (all simulations were implemented with the bssm package) and Helske +#' and Vihola (2021). + +``` + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) +``` + +## Reproducing some of the results of IS-MCMC paper + +Full simulation experiments of Vihola, Helske and Franks (2020) takes some time, +here we only run a single replication to test that the methods work as expected. +To generate Table 1 in the paper, the code below should be run say 1000 times, +from which IREs could be computed. + +```{r, cache = TRUE} +library("bssm") +data(poisson_series) +s <- sd(log(pmax(0.1, poisson_series))) +model <- bsm_ng(poisson_series, sd_level = uniform(0.115, 0, 2 * s), + sd_slope = uniform(0.004, 0, 2 * s), P1 = diag(0.1, 2), distribution = "poisson") + + +d <- data.frame(mean = NA, se = NA, + variable = c("sd_level", "sd_slope", "u_1", "u_100"), + mcmc_type = rep(c("approx", "da", "is1", "is2", "pm"), + times = 4*c(2, 6, 6, 6, 6)), + sampling_method = c(rep("psi", 8), + rep(rep(c("bsf", "spdk", "psi"), each = 2 * 4), 4)), + local_approx = rep(c(TRUE, FALSE), each = 4), + time = NA, + acceptance_rate = NA) + +iter <- 1e4 # Use less iterations than in the paper for faster experiment +for(i in seq(1, nrow(d), by = 4)) { + + cat("Testing method '", d$mcmc_type[i], "' with sampling by '", + d$sampling_method[i], "' and local_approx '", d$local_approx[i], "'\n", + sep = "") + + res <- run_mcmc(model, iter = iter, + sampling_method = d$sampling_method[i], + particles = switch(d$sampling_method[i], + bsf = 200, + spdk = 10, + psi = 10), + mcmc_type = d$mcmc_type[i], + local_approx = d$local_approx[i], + end_adaptive_phase = TRUE) + + w <- res$counts * + if (res$mcmc_type %in% paste0("is", 1:2)) res$weights else 1 + + d[((i - 1) + 1):((i - 1) + 4), "mean"] <- c( + diagis::weighted_mean(res$theta, w), + diagis::weighted_mean(res$alpha[1, 1, ], w), + diagis::weighted_mean(res$alpha[100, 1, ], w)) + + d[((i - 1) + 1):((i - 1) + 4), "se"] <- c( + sqrt(asymptotic_var(res$theta[, 1], w)), + sqrt(asymptotic_var(res$theta[, 2], w)), + sqrt(asymptotic_var(res$alpha[1, 1, ], w)), + sqrt(asymptotic_var(res$alpha[100, 1, ], w))) + + d$time[((i - 1) + 1):((i - 1) + 4)] <- res$time["elapsed"] + d$acceptance_rate[((i - 1) + 1):((i - 1) + 4)] <- res$acceptance_rate +} +``` +Results: +```{r} +library(dplyr) +d |> + arrange(local_approx, variable, mcmc_type, sampling_method) +``` + diff --git a/benchmarks/replications.html b/benchmarks/replications.html new file mode 100644 index 00000000..abe8ed8f --- /dev/null +++ b/benchmarks/replications.html @@ -0,0 +1,530 @@ + + + + + + + + + + + + + + + +Replications + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+

Reproducing some of the results of IS-MCMC paper

+

Full simulation experiments of Vihola, Helske and Franks (2020) takes some time, here we only run a single replication to test that the methods work as expected. To generate Table 1 in the paper, the code below should be run say 1000 times, from which IREs could be computed.

+
library("bssm")
+
## 
+## Attaching package: 'bssm'
+
## The following object is masked from 'package:base':
+## 
+##     gamma
+
data(poisson_series)
+s <- sd(log(pmax(0.1, poisson_series)))
+model <- bsm_ng(poisson_series, sd_level = uniform(0.115, 0, 2 * s),
+  sd_slope = uniform(0.004, 0, 2 * s), P1 = diag(0.1, 2), distribution = "poisson")
+
+
+d <- data.frame(mean = NA, se = NA,
+  variable = c("sd_level", "sd_slope", "u_1", "u_100"),
+  mcmc_type = rep(c("approx", "da", "is1", "is2", "pm"),
+    times = 4*c(2, 6, 6, 6, 6)),
+  sampling_method = c(rep("psi", 8),
+    rep(rep(c("bsf", "spdk", "psi"), each = 2 * 4), 4)),
+  local_approx = rep(c(TRUE, FALSE), each = 4),
+  time = NA,
+  acceptance_rate = NA)
+
+iter <- 1e4 # Use less iterations than in the paper for faster experiment
+for(i in seq(1, nrow(d), by = 4)) {
+  
+  cat("Testing method '", d$mcmc_type[i], "' with sampling by '", 
+    d$sampling_method[i], "' and local_approx '", d$local_approx[i], "'\n", 
+    sep = "")
+  
+  res <- run_mcmc(model, iter = iter,
+    sampling_method = d$sampling_method[i],
+    particles = switch(d$sampling_method[i],
+      bsf = 200,
+      spdk = 10,
+      psi = 10),
+    mcmc_type = d$mcmc_type[i],
+    local_approx = d$local_approx[i],
+    end_adaptive_phase = TRUE)
+  
+  w <- res$counts * 
+    if (res$mcmc_type %in% paste0("is", 1:2)) res$weights else 1
+  
+  d[((i - 1) + 1):((i - 1) + 4), "mean"] <- c(
+    diagis::weighted_mean(res$theta, w),
+    diagis::weighted_mean(res$alpha[1, 1, ], w),
+    diagis::weighted_mean(res$alpha[100, 1, ], w))
+  
+  d[((i - 1) + 1):((i - 1) + 4), "se"] <- c(
+    sqrt(asymptotic_var(res$theta[, 1], w)),
+    sqrt(asymptotic_var(res$theta[, 2], w)),
+    sqrt(asymptotic_var(res$alpha[1, 1, ], w)),
+    sqrt(asymptotic_var(res$alpha[100, 1, ], w)))
+  
+  d$time[((i - 1) + 1):((i - 1) + 4)] <- res$time["elapsed"]
+  d$acceptance_rate[((i - 1) + 1):((i - 1) + 4)] <- res$acceptance_rate
+}
+
## Testing method 'approx' with sampling by 'psi' and local_approx 'TRUE'
+## Testing method 'approx' with sampling by 'psi' and local_approx 'FALSE'
+## Testing method 'da' with sampling by 'bsf' and local_approx 'TRUE'
+## Testing method 'da' with sampling by 'bsf' and local_approx 'FALSE'
+## Testing method 'da' with sampling by 'spdk' and local_approx 'TRUE'
+## Testing method 'da' with sampling by 'spdk' and local_approx 'FALSE'
+## Testing method 'da' with sampling by 'psi' and local_approx 'TRUE'
+## Testing method 'da' with sampling by 'psi' and local_approx 'FALSE'
+## Testing method 'is1' with sampling by 'bsf' and local_approx 'TRUE'
+## Testing method 'is1' with sampling by 'bsf' and local_approx 'FALSE'
+## Testing method 'is1' with sampling by 'spdk' and local_approx 'TRUE'
+## Testing method 'is1' with sampling by 'spdk' and local_approx 'FALSE'
+## Testing method 'is1' with sampling by 'psi' and local_approx 'TRUE'
+## Testing method 'is1' with sampling by 'psi' and local_approx 'FALSE'
+## Testing method 'is2' with sampling by 'bsf' and local_approx 'TRUE'
+## Testing method 'is2' with sampling by 'bsf' and local_approx 'FALSE'
+## Testing method 'is2' with sampling by 'spdk' and local_approx 'TRUE'
+## Testing method 'is2' with sampling by 'spdk' and local_approx 'FALSE'
+## Testing method 'is2' with sampling by 'psi' and local_approx 'TRUE'
+## Testing method 'is2' with sampling by 'psi' and local_approx 'FALSE'
+## Testing method 'pm' with sampling by 'bsf' and local_approx 'TRUE'
+## Testing method 'pm' with sampling by 'bsf' and local_approx 'FALSE'
+## Testing method 'pm' with sampling by 'spdk' and local_approx 'TRUE'
+## Testing method 'pm' with sampling by 'spdk' and local_approx 'FALSE'
+## Testing method 'pm' with sampling by 'psi' and local_approx 'TRUE'
+## Testing method 'pm' with sampling by 'psi' and local_approx 'FALSE'
+

Results:

+
library(dplyr)
+
## 
+## Attaching package: 'dplyr'
+
## The following objects are masked from 'package:stats':
+## 
+##     filter, lag
+
## The following objects are masked from 'package:base':
+## 
+##     intersect, setdiff, setequal, union
+
d %>% 
+    arrange(local_approx, variable, mcmc_type, sampling_method)
+
##            mean           se variable mcmc_type sampling_method local_approx
+## 1    0.09578772 0.0022141806 sd_level    approx             psi        FALSE
+## 2    0.09183384 0.0025847529 sd_level        da             bsf        FALSE
+## 3    0.09056407 0.0034324313 sd_level        da             psi        FALSE
+## 4    0.09693717 0.0019153340 sd_level        da            spdk        FALSE
+## 5    0.09562614 0.0025997452 sd_level       is1             bsf        FALSE
+## 6    0.09303018 0.0027318562 sd_level       is1             psi        FALSE
+## 7    0.09758895 0.0025748789 sd_level       is1            spdk        FALSE
+## 8    0.10105081 0.0025722112 sd_level       is2             bsf        FALSE
+## 9    0.09536685 0.0027073170 sd_level       is2             psi        FALSE
+## 10   0.09590957 0.0023060056 sd_level       is2            spdk        FALSE
+## 11   0.09698759 0.0038890507 sd_level        pm             bsf        FALSE
+## 12   0.09430755 0.0024183804 sd_level        pm             psi        FALSE
+## 13   0.08704774 0.0035083078 sd_level        pm            spdk        FALSE
+## 14   0.01506997 0.0004887884 sd_slope    approx             psi        FALSE
+## 15   0.01622993 0.0007867216 sd_slope        da             bsf        FALSE
+## 16   0.01736272 0.0007476045 sd_slope        da             psi        FALSE
+## 17   0.01542487 0.0004711415 sd_slope        da            spdk        FALSE
+## 18   0.01533332 0.0005808372 sd_slope       is1             bsf        FALSE
+## 19   0.01653758 0.0006161702 sd_slope       is1             psi        FALSE
+## 20   0.01555065 0.0006237624 sd_slope       is1            spdk        FALSE
+## 21   0.01450235 0.0007518923 sd_slope       is2             bsf        FALSE
+## 22   0.01555210 0.0005458160 sd_slope       is2             psi        FALSE
+## 23   0.01522803 0.0005224722 sd_slope       is2            spdk        FALSE
+## 24   0.01526484 0.0008580442 sd_slope        pm             bsf        FALSE
+## 25   0.01726606 0.0005463481 sd_slope        pm             psi        FALSE
+## 26   0.01719711 0.0005954383 sd_slope        pm            spdk        FALSE
+## 27  -0.07869788 0.0105559757      u_1    approx             psi        FALSE
+## 28  -0.06847499 0.0092537409      u_1        da             bsf        FALSE
+## 29  -0.07676795 0.0124369314      u_1        da             psi        FALSE
+## 30  -0.08390600 0.0098000700      u_1        da            spdk        FALSE
+## 31  -0.08702685 0.0125916067      u_1       is1             bsf        FALSE
+## 32  -0.07410507 0.0095352137      u_1       is1             psi        FALSE
+## 33  -0.08393639 0.0114658112      u_1       is1            spdk        FALSE
+## 34  -0.08050907 0.0179799047      u_1       is2             bsf        FALSE
+## 35  -0.07701605 0.0105821573      u_1       is2             psi        FALSE
+## 36  -0.07846330 0.0121624205      u_1       is2            spdk        FALSE
+## 37  -0.09249907 0.0106879249      u_1        pm             bsf        FALSE
+## 38  -0.08991847 0.0111100364      u_1        pm             psi        FALSE
+## 39  -0.06743786 0.0106694815      u_1        pm            spdk        FALSE
+## 40   2.63760608 0.0079707360    u_100    approx             psi        FALSE
+## 41   2.63182502 0.0080169407    u_100        da             bsf        FALSE
+## 42   2.62174704 0.0073447869    u_100        da             psi        FALSE
+## 43   2.61369859 0.0072760694    u_100        da            spdk        FALSE
+## 44   2.62253096 0.0086131210    u_100       is1             bsf        FALSE
+## 45   2.62697081 0.0073913295    u_100       is1             psi        FALSE
+## 46   2.61395948 0.0065218848    u_100       is1            spdk        FALSE
+## 47   2.62980275 0.0117036903    u_100       is2             bsf        FALSE
+## 48   2.62820881 0.0061786231    u_100       is2             psi        FALSE
+## 49   2.61647003 0.0066978454    u_100       is2            spdk        FALSE
+## 50   2.62452635 0.0071852884    u_100        pm             bsf        FALSE
+## 51   2.62372637 0.0064406343    u_100        pm             psi        FALSE
+## 52   2.61946902 0.0053898263    u_100        pm            spdk        FALSE
+## 53   0.09332580 0.0026097129 sd_level    approx             psi         TRUE
+## 54   0.08850704 0.0034589396 sd_level        da             bsf         TRUE
+## 55   0.09495943 0.0026074340 sd_level        da             psi         TRUE
+## 56   0.09321799 0.0025949141 sd_level        da            spdk         TRUE
+## 57   0.08969266 0.0024885812 sd_level       is1             bsf         TRUE
+## 58   0.09524581 0.0026432277 sd_level       is1             psi         TRUE
+## 59   0.09200344 0.0025662615 sd_level       is1            spdk         TRUE
+## 60   0.09164685 0.0029147370 sd_level       is2             bsf         TRUE
+## 61   0.09085042 0.0026602853 sd_level       is2             psi         TRUE
+## 62   0.09401393 0.0023809428 sd_level       is2            spdk         TRUE
+## 63   0.09946007 0.0029061242 sd_level        pm             bsf         TRUE
+## 64   0.09425689 0.0026954951 sd_level        pm             psi         TRUE
+## 65   0.09196438 0.0034327278 sd_level        pm            spdk         TRUE
+## 66   0.01649875 0.0006084374 sd_slope    approx             psi         TRUE
+## 67   0.01629020 0.0007204475 sd_slope        da             bsf         TRUE
+## 68   0.01675024 0.0005625769 sd_slope        da             psi         TRUE
+## 69   0.01596388 0.0005902601 sd_slope        da            spdk         TRUE
+## 70   0.01684928 0.0005048439 sd_slope       is1             bsf         TRUE
+## 71   0.01619417 0.0006082412 sd_slope       is1             psi         TRUE
+## 72   0.01549983 0.0005977432 sd_slope       is1            spdk         TRUE
+## 73   0.01704707 0.0007564945 sd_slope       is2             bsf         TRUE
+## 74   0.01713414 0.0005378904 sd_slope       is2             psi         TRUE
+## 75   0.01581035 0.0006098587 sd_slope       is2            spdk         TRUE
+## 76   0.01493558 0.0007570220 sd_slope        pm             bsf         TRUE
+## 77   0.01681022 0.0005775678 sd_slope        pm             psi         TRUE
+## 78   0.01669934 0.0006237532 sd_slope        pm            spdk         TRUE
+## 79  -0.04763190 0.0098184938      u_1    approx             psi         TRUE
+## 80  -0.06303314 0.0108945672      u_1        da             bsf         TRUE
+## 81  -0.07412832 0.0118281401      u_1        da             psi         TRUE
+## 82  -0.06864390 0.0107736811      u_1        da            spdk         TRUE
+## 83  -0.06413780 0.0102697767      u_1       is1             bsf         TRUE
+## 84  -0.07305205 0.0087975786      u_1       is1             psi         TRUE
+## 85  -0.05281387 0.0087550795      u_1       is1            spdk         TRUE
+## 86  -0.06758727 0.0181045578      u_1       is2             bsf         TRUE
+## 87  -0.08541492 0.0092005051      u_1       is2             psi         TRUE
+## 88  -0.06533748 0.0107268023      u_1       is2            spdk         TRUE
+## 89  -0.07094869 0.0127049274      u_1        pm             bsf         TRUE
+## 90  -0.08475790 0.0099826212      u_1        pm             psi         TRUE
+## 91  -0.07116972 0.0117171069      u_1        pm            spdk         TRUE
+## 92   2.62666325 0.0064783980    u_100    approx             psi         TRUE
+## 93   2.63482193 0.0078166866    u_100        da             bsf         TRUE
+## 94   2.61414177 0.0064441268    u_100        da             psi         TRUE
+## 95   2.61282885 0.0065739149    u_100        da            spdk         TRUE
+## 96   2.61354285 0.0064132143    u_100       is1             bsf         TRUE
+## 97   2.61608214 0.0071682674    u_100       is1             psi         TRUE
+## 98   2.62069831 0.0067225577    u_100       is1            spdk         TRUE
+## 99   2.60570014 0.0103065461    u_100       is2             bsf         TRUE
+## 100  2.62148516 0.0074049226    u_100       is2             psi         TRUE
+## 101  2.61732719 0.0067673451    u_100       is2            spdk         TRUE
+## 102  2.61310072 0.0074142079    u_100        pm             bsf         TRUE
+## 103  2.60707729 0.0068890297    u_100        pm             psi         TRUE
+## 104  2.60921701 0.0070487136    u_100        pm            spdk         TRUE
+##       time acceptance_rate
+## 1     1.04          0.2336
+## 2    16.59          0.2466
+## 3     2.31          0.2068
+## 4     1.65          0.2202
+## 5   143.79          0.2348
+## 6     4.13          0.2238
+## 7     2.34          0.2262
+## 8     8.51          0.2300
+## 9     1.63          0.2470
+## 10    1.26          0.2396
+## 11   65.08          0.2388
+## 12    7.94          0.2354
+## 13    4.34          0.2410
+## 14    1.04          0.2336
+## 15   16.59          0.2466
+## 16    2.31          0.2068
+## 17    1.65          0.2202
+## 18  143.79          0.2348
+## 19    4.13          0.2238
+## 20    2.34          0.2262
+## 21    8.51          0.2300
+## 22    1.63          0.2470
+## 23    1.26          0.2396
+## 24   65.08          0.2388
+## 25    7.94          0.2354
+## 26    4.34          0.2410
+## 27    1.04          0.2336
+## 28   16.59          0.2466
+## 29    2.31          0.2068
+## 30    1.65          0.2202
+## 31  143.79          0.2348
+## 32    4.13          0.2238
+## 33    2.34          0.2262
+## 34    8.51          0.2300
+## 35    1.63          0.2470
+## 36    1.26          0.2396
+## 37   65.08          0.2388
+## 38    7.94          0.2354
+## 39    4.34          0.2410
+## 40    1.04          0.2336
+## 41   16.59          0.2466
+## 42    2.31          0.2068
+## 43    1.65          0.2202
+## 44  143.79          0.2348
+## 45    4.13          0.2238
+## 46    2.34          0.2262
+## 47    8.51          0.2300
+## 48    1.63          0.2470
+## 49    1.26          0.2396
+## 50   65.08          0.2388
+## 51    7.94          0.2354
+## 52    4.34          0.2410
+## 53    3.74          0.2404
+## 54   20.38          0.2408
+## 55    4.83          0.2250
+## 56    4.02          0.2448
+## 57  147.64          0.2434
+## 58    5.78          0.2432
+## 59    4.56          0.2322
+## 60   11.19          0.2510
+## 61    3.89          0.2384
+## 62    3.59          0.2342
+## 63   61.67          0.2428
+## 64   11.47          0.2358
+## 65    6.64          0.2358
+## 66    3.74          0.2404
+## 67   20.38          0.2408
+## 68    4.83          0.2250
+## 69    4.02          0.2448
+## 70  147.64          0.2434
+## 71    5.78          0.2432
+## 72    4.56          0.2322
+## 73   11.19          0.2510
+## 74    3.89          0.2384
+## 75    3.59          0.2342
+## 76   61.67          0.2428
+## 77   11.47          0.2358
+## 78    6.64          0.2358
+## 79    3.74          0.2404
+## 80   20.38          0.2408
+## 81    4.83          0.2250
+## 82    4.02          0.2448
+## 83  147.64          0.2434
+## 84    5.78          0.2432
+## 85    4.56          0.2322
+## 86   11.19          0.2510
+## 87    3.89          0.2384
+## 88    3.59          0.2342
+## 89   61.67          0.2428
+## 90   11.47          0.2358
+## 91    6.64          0.2358
+## 92    3.74          0.2404
+## 93   20.38          0.2408
+## 94    4.83          0.2250
+## 95    4.02          0.2448
+## 96  147.64          0.2434
+## 97    5.78          0.2432
+## 98    4.56          0.2322
+## 99   11.19          0.2510
+## 100   3.89          0.2384
+## 101   3.59          0.2342
+## 102  61.67          0.2428
+## 103  11.47          0.2358
+## 104   6.64          0.2358
+
+ + + + +
+ + + + + + + + + + + + + + + diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..e5a1ab93 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,14 @@ +comment: false + +coverage: + status: + project: + default: + target: auto + threshold: 1% + informational: true + patch: + default: + target: auto + threshold: 1% + informational: true diff --git a/codemeta.json b/codemeta.json new file mode 100644 index 00000000..5ea2691d --- /dev/null +++ b/codemeta.json @@ -0,0 +1,372 @@ +{ + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", + "@type": "SoftwareSourceCode", + "identifier": "bssm", + "description": "Efficient methods for Bayesian inference of state space models via Markov chain Monte Carlo (MCMC) based on parallel importance sampling type weighted estimators (Vihola, Helske, and Franks, 2020, ), particle MCMC, and its delayed acceptance version. Gaussian, Poisson, binomial, negative binomial, and Gamma observation densities and basic stochastic volatility models with linear-Gaussian state dynamics, as well as general non-linear Gaussian models and discretised diffusion models are supported. See Helske and Vihola (2021, ) for details.", + "name": "bssm: Bayesian Inference of Non-Linear and Non-Gaussian State Space\n Models", + "codeRepository": "https://github.com/helske/bssm", + "issueTracker": "https://github.com/helske/bssm/issues", + "license": "https://spdx.org/licenses/GPL-2.0", + "version": "2.0.2", + "programmingLanguage": { + "@type": "ComputerLanguage", + "name": "R", + "url": "https://r-project.org" + }, + "runtimePlatform": "R version 4.3.0 (2023-04-21 ucrt)", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "author": [ + { + "@type": "Person", + "givenName": "Jouni", + "familyName": "Helske", + "email": "jouni.helske@iki.fi", + "@id": "https://orcid.org/0000-0001-7130-793X" + }, + { + "@type": "Person", + "givenName": "Matti", + "familyName": "Vihola", + "@id": "https://orcid.org/0000-0002-8041-7222" + } + ], + "maintainer": [ + { + "@type": "Person", + "givenName": "Jouni", + "familyName": "Helske", + "email": "jouni.helske@iki.fi", + "@id": "https://orcid.org/0000-0001-7130-793X" + } + ], + "softwareSuggestions": [ + { + "@type": "SoftwareApplication", + "identifier": "covr", + "name": "covr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=covr" + }, + { + "@type": "SoftwareApplication", + "identifier": "ggplot2", + "name": "ggplot2", + "version": ">= 2.0.0", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=ggplot2" + }, + { + "@type": "SoftwareApplication", + "identifier": "KFAS", + "name": "KFAS", + "version": ">= 1.2.1", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=KFAS" + }, + { + "@type": "SoftwareApplication", + "identifier": "knitr", + "name": "knitr", + "version": ">= 1.11", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=knitr" + }, + { + "@type": "SoftwareApplication", + "identifier": "MASS", + "name": "MASS", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=MASS" + }, + { + "@type": "SoftwareApplication", + "identifier": "rmarkdown", + "name": "rmarkdown", + "version": ">= 0.8.1", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=rmarkdown" + }, + { + "@type": "SoftwareApplication", + "identifier": "ramcmc", + "name": "ramcmc", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=ramcmc" + }, + { + "@type": "SoftwareApplication", + "identifier": "sde", + "name": "sde", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=sde" + }, + { + "@type": "SoftwareApplication", + "identifier": "sitmo", + "name": "sitmo", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=sitmo" + }, + { + "@type": "SoftwareApplication", + "identifier": "testthat", + "name": "testthat", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=testthat" + } + ], + "softwareRequirements": { + "1": { + "@type": "SoftwareApplication", + "identifier": "R", + "name": "R", + "version": ">= 4.1.0" + }, + "2": { + "@type": "SoftwareApplication", + "identifier": "bayesplot", + "name": "bayesplot", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=bayesplot" + }, + "3": { + "@type": "SoftwareApplication", + "identifier": "checkmate", + "name": "checkmate", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=checkmate" + }, + "4": { + "@type": "SoftwareApplication", + "identifier": "coda", + "name": "coda", + "version": ">= 0.18-1", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=coda" + }, + "5": { + "@type": "SoftwareApplication", + "identifier": "diagis", + "name": "diagis", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=diagis" + }, + "6": { + "@type": "SoftwareApplication", + "identifier": "dplyr", + "name": "dplyr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=dplyr" + }, + "7": { + "@type": "SoftwareApplication", + "identifier": "posterior", + "name": "posterior", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=posterior" + }, + "8": { + "@type": "SoftwareApplication", + "identifier": "Rcpp", + "name": "Rcpp", + "version": ">= 0.12.3", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=Rcpp" + }, + "9": { + "@type": "SoftwareApplication", + "identifier": "rlang", + "name": "rlang", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=rlang" + }, + "10": { + "@type": "SoftwareApplication", + "identifier": "tidyr", + "name": "tidyr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=tidyr" + }, + "SystemRequirements": "pandoc (>= 1.12.3, needed for vignettes)" + }, + "fileSize": "129081.465KB", + "citation": [ + { + "@type": "ScholarlyArticle", + "datePublished": "2021", + "author": [ + { + "@type": "Person", + "givenName": "Jouni", + "familyName": "Helske" + }, + { + "@type": "Person", + "givenName": "Matti", + "familyName": "Vihola" + } + ], + "name": "bssm: Bayesian Inference of Non-linear and Non-Gaussian State Space Models in {R}", + "identifier": "10.32614/RJ-2021-103", + "url": "https://doi.org/10.32614/RJ-2021-103", + "pagination": "578--589", + "@id": "https://doi.org/10.32614/RJ-2021-103", + "sameAs": "https://doi.org/10.32614/RJ-2021-103", + "isPartOf": { + "@type": "PublicationIssue", + "issueNumber": "2", + "datePublished": "2021", + "isPartOf": { + "@type": ["PublicationVolume", "Periodical"], + "volumeNumber": "13", + "name": "R Journal" + } + } + }, + { + "@type": "ScholarlyArticle", + "datePublished": "2020", + "author": [ + { + "@type": "Person", + "givenName": "Matti", + "familyName": "Vihola" + }, + { + "@type": "Person", + "givenName": "Jouni", + "familyName": "Helske" + }, + { + "@type": "Person", + "givenName": "Jordan", + "familyName": "Franks" + } + ], + "name": "Importance Sampling Type Estimators Based on Approximate Marginal {MCMC}", + "identifier": "10.1111/sjos.12492", + "url": "https://onlinelibrary.wiley.com/doi/abs/10.1111/sjos.12492", + "@id": "https://doi.org/10.1111/sjos.12492", + "sameAs": "https://doi.org/10.1111/sjos.12492", + "isPartOf": { + "@type": "PublicationIssue", + "datePublished": "2020", + "isPartOf": { + "@type": ["PublicationVolume", "Periodical"], + "name": "Scandinavian Journal of Statistics" + } + } + } + ], + "releaseNotes": "https://github.com/helske/bssm/blob/master/NEWS.md", + "readme": "https://github.com/helske/bssm/blob/main/README.md", + "contIntegration": ["https://github.com/helske/bssm/actions", "https://app.codecov.io/gh/helske/bssm?branch=master"], + "developmentStatus": "https://www.repostatus.org/#active", + "review": { + "@type": "Review", + "url": "https://github.com/ropensci/software-review/issues/489", + "provider": "https://ropensci.org" + }, + "keywords": ["bayesian-inference", "markov-chain-monte-carlo", "particle-filter", "time-series", "state-space", "r", "cpp"], + "relatedLink": "https://CRAN.R-project.org/package=bssm" +} diff --git a/data/drownings.rda b/data/drownings.rda index 1d4786a7..a1ddde83 100644 Binary files a/data/drownings.rda and b/data/drownings.rda differ diff --git a/data/negbin_model.rda b/data/negbin_model.rda new file mode 100644 index 00000000..44971af0 Binary files /dev/null and b/data/negbin_model.rda differ diff --git a/data/negbin_series.rda b/data/negbin_series.rda new file mode 100644 index 00000000..c999d646 Binary files /dev/null and b/data/negbin_series.rda differ diff --git a/inst/CITATION b/inst/CITATION index 98a6b0ac..edc8ab09 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -1,14 +1,16 @@ c( bibentry( - bibtype = "misc", + bibtype = "Article", key = "helske-vihola2021", - title= "bssm: Bayesian Inference of Non-linear and Non-Gaussian State Space Models in {R}", - author="Jouni Helske and Matti Vihola", - year="2021", - eprint="2101.08492", - archivePrefix="arXiv", - primaryClass="stat.CO", - url = "https://arxiv.org/abs/2101.08492" + title = "bssm: Bayesian Inference of Non-linear and Non-Gaussian State Space Models in {R}", + author =" Jouni Helske and Matti Vihola", + year= "2021", + doi = "10.32614/RJ-2021-103", + journal = "R Journal", + url = "https://doi.org/10.32614/RJ-2021-103", + pages = "578--589", + volume = "13", + number = "2" ), bibentry( bibtype = "Article", @@ -20,4 +22,4 @@ c( year = "2020", key = "vihola-helske-franks" ) -) \ No newline at end of file +) diff --git a/man/ar1_lg.Rd b/man/ar1_lg.Rd index 9ee0c96b..106bed3a 100644 --- a/man/ar1_lg.Rd +++ b/man/ar1_lg.Rd @@ -7,30 +7,55 @@ ar1_lg(y, rho, sigma, mu, sd_y, beta, xreg = NULL) } \arguments{ -\item{y}{Vector or a \code{\link{ts}} object of observations.} +\item{y}{A vector or a \code{ts} object of observations.} -\item{rho}{prior for autoregressive coefficient.} +\item{rho}{A prior for autoregressive coefficient. +Should be an object of class \code{bssm_prior}.} -\item{sigma}{Prior for the standard deviation of noise of the AR-process.} +\item{sigma}{A prior for the standard deviation of noise of the AR-process. +Should be an object of class \code{bssm_prior}} -\item{mu}{A fixed value or a prior for the stationary mean of the latent AR(1) process. Parameter is omitted if this is set to 0.} +\item{mu}{A fixed value or a prior for the stationary mean of the latent +AR(1) process. Should be an object of class \code{bssm_prior} or scalar +value defining a fixed mean such as 0.} -\item{sd_y}{Prior for the standard deviation of observation equation.} +\item{sd_y}{A prior for the standard deviation of observation equation.} -\item{beta}{Prior for the regression coefficients.} +\item{beta}{A prior for the regression coefficients. +Should be an object of class \code{bssm_prior} or \code{bssm_prior_list} +(in case of multiple coefficients) or missing in case of no covariates.} -\item{xreg}{Matrix containing covariates.} +\item{xreg}{A matrix containing covariates with number of rows matching the +length of \code{y}. Can also be \code{ts}, \code{mts} or similar object +convertible to matrix.} } \value{ -Object of class \code{ar1_lg}. +An object of class \code{ar1_lg}. } \description{ -Constructs a simple Gaussian model where the state dynamics follow an AR(1) process. +Constructs a simple Gaussian model where the state dynamics +follow an AR(1) process. } \examples{ -model <- ar1_lg(BJsales, rho = uniform(0.5,-1,1), - sigma = halfnormal(1, 10), mu = normal(200, 200, 100), - sd_y = halfnormal(1, 10)) +set.seed(1) +mu <- 2 +rho <- 0.7 +sd_y <- 0.1 +sigma <- 0.5 +beta <- -1 +x <- rnorm(30) +z <- y <- numeric(30) +z[1] <- rnorm(1, mu, sigma / sqrt(1 - rho^2)) +y[1] <- rnorm(1, beta * x[1] + z[1], sd_y) +for(i in 2:30) { + z[i] <- rnorm(1, mu * (1 - rho) + rho * z[i - 1], sigma) + y[i] <- rnorm(1, beta * x[i] + z[i], sd_y) +} +model <- ar1_lg(y, rho = uniform(0.5, -1, 1), + sigma = halfnormal(1, 10), mu = normal(0, 0, 1), + sd_y = halfnormal(1, 10), + xreg = x, beta = normal(0, 0, 1)) out <- run_mcmc(model, iter = 2e4) summary(out, return_se = TRUE) + } diff --git a/man/ar1_ng.Rd b/man/ar1_ng.Rd index 9aa1e85b..44a66c28 100644 --- a/man/ar1_ng.Rd +++ b/man/ar1_ng.Rd @@ -4,35 +4,78 @@ \alias{ar1_ng} \title{Non-Gaussian model with AR(1) latent process} \usage{ -ar1_ng(y, rho, sigma, mu, distribution, phi, u = 1, beta, xreg = NULL) +ar1_ng(y, rho, sigma, mu, distribution, phi, u, beta, xreg = NULL) } \arguments{ -\item{y}{Vector or a \code{\link{ts}} object of observations.} +\item{y}{A vector or a \code{ts} object of observations.} -\item{rho}{prior for autoregressive coefficient.} +\item{rho}{A prior for autoregressive coefficient. +Should be an object of class \code{bssm_prior}.} -\item{sigma}{Prior for the standard deviation of noise of the AR-process.} +\item{sigma}{A prior for the standard deviation of noise of the AR-process. +Should be an object of class \code{bssm_prior}} -\item{mu}{A fixed value or a prior for the stationary mean of the latent AR(1) process. Parameter is omitted if this is set to 0.} +\item{mu}{A fixed value or a prior for the stationary mean of the latent +AR(1) process. Should be an object of class \code{bssm_prior} or scalar +value defining a fixed mean such as 0.} -\item{distribution}{Distribution of the observed time series. Possible choices are -\code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and \code{"negative binomial"}.} +\item{distribution}{Distribution of the observed time series. Possible +choices are \code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and +\code{"negative binomial"}.} \item{phi}{Additional parameter relating to the non-Gaussian distribution. -For negative binomial distribution this is the dispersion term, for gamma distribution -this is the shape parameter, and for other distributions this is ignored.} +For negative binomial distribution this is the dispersion term, for gamma +distribution this is the shape parameter, and for other distributions this +is ignored. Should an object of class \code{bssm_prior} or +a positive scalar.} -\item{u}{Constant parameter vector for non-Gaussian models. For Poisson, gamma, and -negative binomial distribution, this corresponds to the offset term. For binomial, -this is the number of trials.} +\item{u}{A vector of positive constants for non-Gaussian models. For +Poisson, gamma, and negative binomial distribution, this corresponds to the +offset term. For binomial, this is the number of trials.} -\item{beta}{Prior for the regression coefficients.} +\item{beta}{A prior for the regression coefficients. +Should be an object of class \code{bssm_prior} or \code{bssm_prior_list} +(in case of multiple coefficients) or missing in case of no covariates.} -\item{xreg}{Matrix containing covariates.} +\item{xreg}{A matrix containing covariates with number of rows matching the +length of \code{y}. Can also be \code{ts}, \code{mts} or similar object +convertible to matrix.} } \value{ -Object of class \code{ar1_ng}. +An object of class \code{ar1_ng}. } \description{ -Constructs a simple non-Gaussian model where the state dynamics follow an AR(1) process. +Constructs a simple non-Gaussian model where the state dynamics follow an +AR(1) process. +} +\examples{ +model <- ar1_ng(discoveries, rho = uniform(0.5,-1,1), + sigma = halfnormal(0.1, 1), mu = normal(0, 0, 1), + distribution = "poisson") +out <- run_mcmc(model, iter = 1e4, mcmc_type = "approx", + output_type = "summary") + +ts.plot(cbind(discoveries, exp(out$alphahat)), col = 1:2) + +set.seed(1) +n <- 30 +phi <- 2 +rho <- 0.9 +sigma <- 0.1 +beta <- 0.5 +u <- rexp(n, 0.1) +x <- rnorm(n) +z <- y <- numeric(n) +z[1] <- rnorm(1, 0, sigma / sqrt(1 - rho^2)) +y[1] <- rnbinom(1, mu = u * exp(beta * x[1] + z[1]), size = phi) +for(i in 2:n) { + z[i] <- rnorm(1, rho * z[i - 1], sigma) + y[i] <- rnbinom(1, mu = u * exp(beta * x[i] + z[i]), size = phi) +} + +model <- ar1_ng(y, rho = uniform_prior(0.9, 0, 1), + sigma = gamma_prior(0.1, 2, 10), mu = 0., + phi = gamma_prior(2, 2, 1), distribution = "negative binomial", + xreg = x, beta = normal_prior(0.5, 0, 1), u = u) + } diff --git a/man/as.data.frame.mcmc_output.Rd b/man/as.data.frame.mcmc_output.Rd index b80105fc..816f6011 100644 --- a/man/as.data.frame.mcmc_output.Rd +++ b/man/as.data.frame.mcmc_output.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/as.data.frame.mcmc_output.R \name{as.data.frame.mcmc_output} \alias{as.data.frame.mcmc_output} -\title{Convert MCMC chain to data.frame} +\title{Convert MCMC Output to data.frame} \usage{ \method{as.data.frame}{mcmc_output}( x, @@ -11,32 +11,40 @@ variable = c("theta", "states"), times, states, - expand = !(x$mcmc_type \%in\% paste0("is", 1:3)), + expand = TRUE, + use_times = TRUE, ... ) } \arguments{ -\item{x}{Output from \code{\link{run_mcmc}}.} +\item{x}{Object of class \code{mcmc_output} from \code{\link{run_mcmc}}.} \item{row.names}{Ignored.} \item{optional}{Ignored.} -\item{variable}{Return samples of \code{"theta"} (default) or \code{"states"}?} +\item{variable}{Return samples of \code{"theta"} (default) or +\code{"states"}?} -\item{times}{Vector of indices. In case of states, what time points to return? Default is all.} +\item{times}{A vector of indices. In case of states, +what time points to return? Default is all.} -\item{states}{Vector of indices. In case of states, what states to return? Default is all.} +\item{states}{A vector of indices. In case of states, +what states to return? Default is all.} -\item{expand}{Should the jump-chain be expanded? -Defaults to \code{TRUE} for non-IS-MCMC, and \code{FALSE} for IS-MCMC. -For \code{expand = FALSE} and always for IS-MCMC, -the resulting data.frame contains variable weight (= counts times IS-weights).} +\item{expand}{Should the jump-chain be expanded? +Defaults to \code{TRUE}. +For \code{expand = FALSE} and always for IS-MCMC, +the resulting data.frame contains variable weight (= counts * IS-weights).} + +\item{use_times}{If \code{TRUE} (default), transforms the values of the time +variable to match the ts attribute of the input to define. If \code{FALSE}, +time is based on the indexing starting from 1.} \item{...}{Ignored.} } \description{ -Converts the MCMC chain output of \code{\link{run_mcmc}} to data.frame. +Converts the MCMC output of \code{\link{run_mcmc}} to \code{data.frame}. } \examples{ data("poisson_series") @@ -53,7 +61,12 @@ head(as.data.frame(out, variable = "state")) head(as.data.frame(out, variable = "theta", expand = FALSE)) # IS-weighted version: -out_is <- run_mcmc(model, iter = 2000, particles = 10, mcmc_type = "is2") +out_is <- run_mcmc(model, iter = 2000, particles = 10, + mcmc_type = "is2") head(as.data.frame(out_is, variable = "theta")) } +\seealso{ +\code{as_draws} which converts the output for +\code{as_draws} object. +} diff --git a/man/as_bssm.Rd b/man/as_bssm.Rd index 6894be8d..2670e054 100644 --- a/man/as_bssm.Rd +++ b/man/as_bssm.Rd @@ -13,21 +13,26 @@ as_bssm(model, kappa = 100, ...) used to replace exact diffuse elements of the original model.} \item{...}{Additional arguments to model building functions of \code{bssm} -(such as prior and updating functions).} +(such as prior and updating functions, C, and D).} } \value{ -Object of class \code{ssm_ulg}, \code{ssm_mlg}, \code{ssm_ung} or \code{ssm_mng}. +An object of class \code{ssm_ulg}, \code{ssm_mlg}, \code{ssm_ung} or +\code{ssm_mng}. } \description{ -Converts \code{SSModel} object of \code{KFAS} package to general -\code{bssm} model of type \code{ssm_ulg}, \code{ssm_mlg}, \code{ssm_ung} or \code{ssm_mng}. +Converts \code{SSModel} object of \code{KFAS} package to general \code{bssm} +model of type \code{ssm_ulg}, \code{ssm_mlg}, \code{ssm_ung} or +\code{ssm_mng}. As \code{KFAS} supports formula syntax for defining +e.g. regression and cyclic components it maybe sometimes easier to define +the model with \code{KFAS::SSModel} and then convert for the bssm style with +\code{as_bssm}. } \examples{ library("KFAS") -model_KFAS <- SSModel(Nile ~ - SSMtrend(1, Q = 2, P1 = 1e4), H = 2) -model_bssm <- as_bssm(model_KFAS) -logLik(model_KFAS) -logLik(model_bssm) + model_KFAS <- SSModel(Nile ~ + SSMtrend(1, Q = 2, P1 = 1e4), H = 2) + model_bssm <- as_bssm(model_KFAS) + logLik(model_KFAS) + logLik(model_bssm) } diff --git a/man/as_draws-mcmc_output.Rd b/man/as_draws-mcmc_output.Rd new file mode 100644 index 00000000..c75bff58 --- /dev/null +++ b/man/as_draws-mcmc_output.Rd @@ -0,0 +1,67 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/as_draws.R +\name{as_draws_df.mcmc_output} +\alias{as_draws_df.mcmc_output} +\alias{as_draws} +\alias{as_draws_df} +\alias{as_draws.mcmc_output} +\title{Convert \code{run_mcmc} Output to \code{draws_df} Format} +\usage{ +\method{as_draws_df}{mcmc_output}(x, times, states, ...) + +\method{as_draws}{mcmc_output}(x, times, states, ...) +} +\arguments{ +\item{x}{An object of class \code{mcmc_output}.} + +\item{times}{A vector of indices defining which time points to return? +Default is all. If 0, no samples for the states are extracted.} + +\item{states}{A vector of indices defining which states to return. +Default is all. If 0, no samples for the states are extracted.} + +\item{...}{Ignored.} +} +\value{ +A \code{draws_df} object. +} +\description{ +Converts MCMC output from \code{run_mcmc} call to a +\code{draws_df} format of the \code{posterior} package. This enables the use +of diagnostics and plotting methods of \code{posterior} and \code{bayesplot} +packages. +} +\note{ +The jump chain representation is automatically expanded by +\code{as_draws}, but if \code{run_mcmc} used IS-MCMC method, the output +contains additional \code{weight} column corresponding to the IS-weights +(without counts), which is ignored by \code{posterior} and \code{bayesplot}, +i.e. those results correspond to approximate MCMC. +} +\examples{ + +model <- bsm_lg(Nile, + sd_y = tnormal(init = 100, mean = 100, sd = 100, min = 0), + sd_level = tnormal(init = 50, mean = 50, sd = 100, min = 0), + a1 = 1000, P1 = 500^2) + +fit1 <- run_mcmc(model, iter = 2000) +draws <- as_draws(fit1) +head(draws, 4) +estimate_ess(draws$sd_y) +summary(fit1, return_se = TRUE) + +# More chains: +model$theta[] <- c(50, 150) # change initial value +fit2 <- run_mcmc(model, iter = 2000, verbose = FALSE) +model$theta[] <- c(150, 50) # change initial value +fit3 <- run_mcmc(model, iter = 2000, verbose = FALSE) + +# it is actually enough to transform first mcmc_output to draws object, +# rest are transformed automatically inside bind_draws +draws <- posterior::bind_draws(as_draws(fit1), + as_draws(fit2), as_draws(fit3), along = "chain") + +posterior::rhat(draws$sd_y) + +} diff --git a/man/asymptotic_var.Rd b/man/asymptotic_var.Rd index 729db956..5c8428ef 100644 --- a/man/asymptotic_var.Rd +++ b/man/asymptotic_var.Rd @@ -1,17 +1,64 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/print_mcmc.R +% Please edit documentation in R/asymptotic_var.R \name{asymptotic_var} \alias{asymptotic_var} \title{Asymptotic Variance of IS-type Estimators} \usage{ -asymptotic_var(x, w) +asymptotic_var(x, w, method = "sokal") } \arguments{ -\item{x}{Vector of samples.} +\item{x}{A numeric vector of samples.} -\item{w}{Vector of weights.} +\item{w}{A numeric vector of weights. If missing, set to 1 (i.e. no +weighting is assumed).} + +\item{method}{Method for computing IACT. Default is \code{"sokal"}, +other option \code{"geyer"}.} +} +\value{ +A single numeric value of asymptotic variance estimate. } \description{ -Estimates the asymptotic variance based on Corollary 1 -of Vihola et al. (2020) from weighted samples from IS-MCMC. +The asymptotic variance MCMCSE^2 is based on Corollary 1 +of Vihola et al. (2020) from weighted samples from IS-MCMC. The default +method is based on the integrated autocorrelation time (IACT) by Sokal +(1997) which seem to work well for reasonable problems, but it is also +possible to use the Geyer's method as implemented in \code{ess_mean} of the +\code{posterior} package. +} +\examples{ +set.seed(1) +n <- 1e4 +x <- numeric(n) +phi <- 0.7 +for(t in 2:n) x[t] <- phi * x[t-1] + rnorm(1) +w <- rexp(n, 0.5 * exp(0.001 * x^2)) +# different methods: +asymptotic_var(x, w, method = "sokal") +asymptotic_var(x, w, method = "geyer") + +data("negbin_model") +# can be obtained directly with summary method +d <- suppressWarnings(as_draws(negbin_model)) +sqrt(asymptotic_var(d$sd_level, d$weight)) + +} +\references{ +Vihola M, Helske J, Franks J. (2020). Importance sampling type estimators +based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 + +Sokal A. (1997). Monte Carlo Methods in Statistical Mechanics: Foundations +and New Algorithms. +In: DeWitt-Morette C, Cartier P, Folacci A (eds) Functional Integration. +NATO ASI Series (Series B: Physics), vol 361. Springer, Boston, MA. +https://doi.org/10.1007/978-1-4899-0319-8_6 + +Gelman, A, Carlin J B, Stern H S, Dunson, D B, Vehtari A, Rubin D B. (2013). +Bayesian Data Analysis, Third Edition. Chapman and Hall/CRC. + +Vehtari A, Gelman A, Simpson D, Carpenter B, Bürkner P-C. (2021). +Rank-normalization, folding, and localization: An improved Rhat for +assessing convergence of MCMC. Bayesian analysis, 16(2):667-718. +https://doi.org/10.1214/20-BA1221 } diff --git a/man/bootstrap_filter.Rd b/man/bootstrap_filter.Rd index 7d255978..b352824e 100644 --- a/man/bootstrap_filter.Rd +++ b/man/bootstrap_filter.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/bootstrap_filter.R \name{bootstrap_filter} \alias{bootstrap_filter} -\alias{bootstrap_filter.gaussian} +\alias{bootstrap_filter.lineargaussian} \alias{bootstrap_filter.nongaussian} \alias{bootstrap_filter.ssm_nlg} \alias{bootstrap_filter.ssm_sde} @@ -10,7 +10,7 @@ \usage{ bootstrap_filter(model, particles, ...) -\method{bootstrap_filter}{gaussian}( +\method{bootstrap_filter}{lineargaussian}( model, particles, seed = sample(.Machine$integer.max, size = 1), @@ -40,24 +40,29 @@ bootstrap_filter(model, particles, ...) ) } \arguments{ -\item{model}{of class \code{bsm_lg}, \code{bsm_ng} or \code{svm}.} +\item{model}{A model object of class \code{bssm_model}.} -\item{particles}{Number of particles.} +\item{particles}{Number of particles as a positive integer. Suitable values +depend on the model and the data, and while larger values provide more +accurate estimates, the run time also increases with respect to the +number of particles, so it is generally a good idea to test the filter first +with a small number of particles, e.g., less than 100.} \item{...}{Ignored.} -\item{seed}{Seed for RNG.} +\item{seed}{Seed for the C++ RNG (positive integer).} -\item{L}{Integer defining the discretization level for SDE models.} +\item{L}{Positive integer defining the discretization level for SDE models.} } \value{ -List with samples (\code{alpha}) from the filtering distribution and corresponding weights (\code{weights}), - as well as filtered and predicted states and corresponding covariances (\code{at}, \code{att}, \code{Pt}, \code{Ptt}), - and estimated log-likelihood (\code{logLik}). +List with samples (\code{alpha}) from the filtering distribution and +corresponding weights (\code{weights}), as well as filtered and predicted +states and corresponding covariances (\code{at}, \code{att}, \code{Pt}, +\code{Ptt}), and estimated log-likelihood (\code{logLik}). } \description{ -Function \code{bootstrap_filter} performs a bootstrap filtering with stratification -resampling. +Function \code{bootstrap_filter} performs a bootstrap filtering with +stratification resampling. } \examples{ set.seed(1) @@ -78,6 +83,7 @@ ts.plot(cbind(poisson_series, exp(out$att[, 1])), col = 1:2) } \references{ -Gordon, N. J., Salmond, D. J., & Smith, A. F. M. (1993). -Novel approach to nonlinear/non-Gaussian Bayesian state estimation. IEE Proceedings-F, 140, 107–113. +Gordon, NJ, Salmond, DJ, Smith, AFM (1993) Novel approach to +nonlinear/non-Gaussian Bayesian state estimation. IEE Proceedings F, +140(2), p. 107-113. } diff --git a/man/bsm_lg.Rd b/man/bsm_lg.Rd index fc755634..c082cb21 100644 --- a/man/bsm_lg.Rd +++ b/man/bsm_lg.Rd @@ -12,59 +12,90 @@ bsm_lg( sd_seasonal, beta, xreg = NULL, - period = frequency(y), - a1, - P1, - D, - C + period, + a1 = NULL, + P1 = NULL, + D = NULL, + C = NULL ) } \arguments{ -\item{y}{Vector or a \code{\link{ts}} object of observations.} +\item{y}{A vector or a \code{ts} object of observations.} -\item{sd_y}{A fixed value or prior for the standard error of -observation equation. See \link[=uniform]{priors} for details.} +\item{sd_y}{Standard deviation of the noise of observation equation. +Should be an object of class \code{bssm_prior} or scalar +value defining a known value such as 0.} -\item{sd_level}{A fixed value or a prior for the standard error -of the noise in level equation. See \link[=uniform]{priors} for details.} +\item{sd_level}{Standard deviation of the noise of level equation. +Should be an object of class \code{bssm_prior} or scalar +value defining a known value such as 0.} -\item{sd_slope}{A fixed value or a prior for the standard error -of the noise in slope equation. See \link[=uniform]{priors} for details. -If missing, the slope term is omitted from the model.} +\item{sd_slope}{Standard deviation of the noise of slope equation. +Should be an object of class \code{bssm_prior}, scalar +value defining a known value such as 0, or missing, in which case the slope +term is omitted from the model.} -\item{sd_seasonal}{A fixed value or a prior for the standard error -of the noise in seasonal equation. See \link[=uniform]{priors} for details. -If missing, the seasonal component is omitted from the model.} +\item{sd_seasonal}{Standard deviation of the noise of seasonal equation. +Should be an object of class \code{bssm_prior}, scalar +value defining a known value such as 0, or missing, in which case the +seasonal term is omitted from the model.} -\item{beta}{Prior for the regression coefficients.} +\item{beta}{A prior for the regression coefficients. +Should be an object of class \code{bssm_prior} or \code{bssm_prior_list} +(in case of multiple coefficients) or missing in case of no covariates.} -\item{xreg}{Matrix containing covariates.} +\item{xreg}{A matrix containing covariates with number of rows matching the +length of \code{y}. Can also be \code{ts}, \code{mts} or similar object +convertible to matrix.} -\item{period}{Length of the seasonal component i.e. the number of} +\item{period}{Length of the seasonal pattern. +Must be a positive value greater than 2 and less than the length of the +input time series. Default is \code{frequency(y)}, +which can also return non-integer value (in which case error is given).} \item{a1}{Prior means for the initial states (level, slope, seasonals). Defaults to vector of zeros.} -\item{P1}{Prior covariance for the initial states (level, slope, seasonals). -Default is diagonal matrix with 1000 on the diagonal.} +\item{P1}{Prior covariance matrix for the initial states (level, slope, +seasonals).Default is diagonal matrix with 100 on the diagonal.} -\item{D, C}{Intercept terms for observation and -state equations, given as a length n vector and m times n matrix respectively.} +\item{D}{Intercept terms for observation equation, given as a length n +numeric vector or a scalar in case of time-invariant intercept.} + +\item{C}{Intercept terms for state equation, given as a m times n matrix +or m times 1 matrix in case of time-invariant intercept.} } \value{ -Object of class \code{bsm_lg}. +An object of class \code{bsm_lg}. } \description{ -Constructs a basic structural model with local level or local trend component -and seasonal component. +Constructs a basic structural model with local level or local trend +component and seasonal component. } \examples{ +set.seed(1) +n <- 50 +x <- rnorm(n) +level <- numeric(n) +level[1] <- rnorm(1) +for (i in 2:n) level[i] <- rnorm(1, -0.2 + level[i-1], sd = 0.1) +y <- rnorm(n, 2.1 + x + level) +model <- bsm_lg(y, sd_y = halfnormal(1, 5), sd_level = 0.1, a1 = level[1], + P1 = matrix(0, 1, 1), xreg = x, beta = normal(1, 0, 1), + D = 2.1, C = matrix(-0.2, 1, 1)) + +ts.plot(cbind(fast_smoother(model), level), col = 1:2) + prior <- uniform(0.1 * sd(log10(UKgas)), 0, 1) -model <- bsm_lg(log10(UKgas), sd_y = prior, sd_level = prior, - sd_slope = prior, sd_seasonal = prior) +# period here is redundant as frequency(UKgas) = 4 +model_UKgas <- bsm_lg(log10(UKgas), sd_y = prior, sd_level = prior, + sd_slope = prior, sd_seasonal = prior, period = 4) -mcmc_out <- run_mcmc(model, iter = 5000) +# Note small number of iterations for CRAN checks +mcmc_out <- run_mcmc(model_UKgas, iter = 5000) +summary(mcmc_out, return_se = TRUE) +# Use the summary method from coda: summary(expand_sample(mcmc_out, "theta"))$stat mcmc_out$theta[which.max(mcmc_out$posterior), ] sqrt((fit <- StructTS(log10(UKgas), type = "BSM"))$coef)[c(4, 1:3)] diff --git a/man/bsm_ng.Rd b/man/bsm_ng.Rd index c81bc07b..d8459765 100644 --- a/man/bsm_ng.Rd +++ b/man/bsm_ng.Rd @@ -12,61 +12,74 @@ bsm_ng( sd_noise, distribution, phi, - u = 1, + u, beta, xreg = NULL, - period = frequency(y), - a1, - P1, - C + period, + a1 = NULL, + P1 = NULL, + C = NULL ) } \arguments{ -\item{y}{Vector or a \code{\link{ts}} object of observations.} +\item{y}{A vector or a \code{ts} object of observations.} -\item{sd_level}{A fixed value or a prior for the standard error -of the noise in level equation. See \link[=uniform]{priors} for details.} +\item{sd_level}{Standard deviation of the noise of level equation. +Should be an object of class \code{bssm_prior} or scalar +value defining a known value such as 0.} -\item{sd_slope}{A fixed value or a prior for the standard error -of the noise in slope equation. See \link[=uniform]{priors} for details. -If missing, the slope term is omitted from the model.} +\item{sd_slope}{Standard deviation of the noise of slope equation. +Should be an object of class \code{bssm_prior}, scalar +value defining a known value such as 0, or missing, in which case the slope +term is omitted from the model.} -\item{sd_seasonal}{A fixed value or a prior for the standard error -of the noise in seasonal equation. See \link[=uniform]{priors} for details. -If missing, the seasonal component is omitted from the model.} +\item{sd_seasonal}{Standard deviation of the noise of seasonal equation. +Should be an object of class \code{bssm_prior}, scalar +value defining a known value such as 0, or missing, in which case the +seasonal term is omitted from the model.} -\item{sd_noise}{Prior for the standard error of the additional noise term. -See \link[=uniform]{priors} for details. If missing, no additional noise term is used.} +\item{sd_noise}{A prior for the standard deviation of the additional noise +term to be added to linear predictor, defined as an object of class +\code{bssm_prior}. If missing, no additional noise term is used.} -\item{distribution}{Distribution of the observed time series. Possible choices are -\code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and \code{"negative binomial"}.} +\item{distribution}{Distribution of the observed time series. Possible +choices are \code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and +\code{"negative binomial"}.} \item{phi}{Additional parameter relating to the non-Gaussian distribution. -For negative binomial distribution this is the dispersion term, for gamma distribution -this is the shape parameter, and for other distributions this is ignored.} +For negative binomial distribution this is the dispersion term, for gamma +distribution this is the shape parameter, and for other distributions this +is ignored. Should an object of class \code{bssm_prior} or +a positive scalar.} -\item{u}{Constant parameter vector for non-Gaussian models. For Poisson, gamma, and -negative binomial distribution, this corresponds to the offset term. For binomial, -this is the number of trials.} +\item{u}{A vector of positive constants for non-Gaussian models. For +Poisson, gamma, and negative binomial distribution, this corresponds to the +offset term. For binomial, this is the number of trials.} -\item{beta}{Prior for the regression coefficients.} +\item{beta}{A prior for the regression coefficients. +Should be an object of class \code{bssm_prior} or \code{bssm_prior_list} +(in case of multiple coefficients) or missing in case of no covariates.} -\item{xreg}{Matrix containing covariates.} +\item{xreg}{A matrix containing covariates with number of rows matching the +length of \code{y}. Can also be \code{ts}, \code{mts} or similar object +convertible to matrix.} -\item{period}{Length of the seasonal component i.e. the number of -observations per season. Default is \code{frequency(y)}.} +\item{period}{Length of the seasonal pattern. +Must be a positive value greater than 2 and less than the length of the +input time series. Default is \code{frequency(y)}, +which can also return non-integer value (in which case error is given).} \item{a1}{Prior means for the initial states (level, slope, seasonals). Defaults to vector of zeros.} -\item{P1}{Prior covariance for the initial states (level, slope, seasonals). -Default is diagonal matrix with 1e5 on the diagonal.} +\item{P1}{Prior covariance matrix for the initial states (level, slope, +seasonals).Default is diagonal matrix with 100 on the diagonal.} -\item{C}{Intercept terms for state equation, given as a -m times n matrix.} +\item{C}{Intercept terms for state equation, given as a m x n or m x 1 +matrix.} } \value{ -Object of class \code{bsm_ng}. +An object of class \code{bsm_ng}. } \description{ Constructs a non-Gaussian basic structural model with local level or @@ -74,14 +87,37 @@ local trend component, a seasonal component, and regression component (or subset of these components). } \examples{ +# Same data as in Vihola, Helske, Franks (2020) +data(poisson_series) +s <- sd(log(pmax(0.1, poisson_series))) +model <- bsm_ng(poisson_series, sd_level = uniform(0.115, 0, 2 * s), + sd_slope = uniform(0.004, 0, 2 * s), P1 = diag(0.1, 2), + distribution = "poisson") + +\donttest{ +out <- run_mcmc(model, iter = 1e5, particles = 10) +summary(out, variable = "theta", return_se = TRUE) +# should be about 0.093 and 0.016 +summary(out, variable = "states", return_se = TRUE, + states = 1, times = c(1, 100)) +# should be about -0.075, 2.618 +} + model <- bsm_ng(Seatbelts[, "VanKilled"], distribution = "poisson", sd_level = halfnormal(0.01, 1), sd_seasonal = halfnormal(0.01, 1), beta = normal(0, 0, 10), - xreg = Seatbelts[, "law"]) -\dontrun{ + xreg = Seatbelts[, "law"], + # default values, just for illustration + period = 12L, + a1 = rep(0, 1 + 11), # level + period - 1 seasonal states + P1 = diag(1, 12), + C = matrix(0, 12, 1), + u = rep(1, nrow(Seatbelts))) + +\donttest{ set.seed(123) -mcmc_out <- run_mcmc(model, iter = 5000, particles = 10) +mcmc_out <- run_mcmc(model, iter = 5000, particles = 10, mcmc_type = "da") mcmc_out$acceptance_rate theta <- expand_sample(mcmc_out, "theta") plot(theta) @@ -92,11 +128,32 @@ ggplot(as.data.frame(theta[,1:2]), aes(x = sd_level, y = sd_seasonal)) + geom_point() + stat_density2d(aes(fill = ..level.., alpha = ..level..), geom = "polygon") + scale_fill_continuous(low = "green", high = "blue") + guides(alpha = "none") - -# Traceplot using as.data.frame method for MCMC output: + +# Traceplot using as.data.frame method for MCMC output library("dplyr") -as.data.frame(mcmc_out) \%>\% - filter(variable == "sd_level") \%>\% +as.data.frame(mcmc_out) |> + filter(variable == "sd_level") |> ggplot(aes(y = value, x = iter)) + geom_line() + } +# Model with slope term and additional noise to linear predictor to capture +# excess variation +model2 <- bsm_ng(Seatbelts[, "VanKilled"], distribution = "poisson", + sd_level = halfnormal(0.01, 1), + sd_seasonal = halfnormal(0.01, 1), + beta = normal(0, 0, 10), + xreg = Seatbelts[, "law"], + sd_slope = halfnormal(0.01, 0.1), + sd_noise = halfnormal(0.01, 1)) + +# instead of extra noise term, model using negative binomial distribution: +model3 <- bsm_ng(Seatbelts[, "VanKilled"], + distribution = "negative binomial", + sd_level = halfnormal(0.01, 1), + sd_seasonal = halfnormal(0.01, 1), + beta = normal(0, 0, 10), + xreg = Seatbelts[, "law"], + sd_slope = halfnormal(0.01, 0.1), + phi = gamma_prior(1, 5, 5)) + } diff --git a/man/bssm.Rd b/man/bssm.Rd index e6bf721d..09d3377c 100644 --- a/man/bssm.Rd +++ b/man/bssm.Rd @@ -3,17 +3,120 @@ \docType{package} \name{bssm} \alias{bssm} +\alias{bssm-package} \title{Bayesian Inference of State Space Models} \description{ -This package contains functions for Bayesian inference of basic stochastic volatility model -and exponential family state space models, where the state equation is linear and Gaussian, -and the conditional observation density is either Gaussian, Poisson, -binomial, negative binomial or Gamma density. General non-linear Gaussian models and models -with continuous SDE dynamics are also supported. For formal definition of the -currently supported models and methods, as well as some theory behind the IS-MCMC and \eqn{\psi}{psi}-APF, -see the package vignettes and Vihola, Helske, Franks (2020). +This package contains functions for efficient Bayesian inference of state +space models (SSMs). For details, see the package vignette and the R Journal +paper. +} +\details{ +The model is assumed to be either +\itemize{ +\item Exponential family state space model, where the state equation is linear +Gaussian, and the conditional observation density is either Gaussian, +Poisson, binomial, negative binomial or Gamma density. +\item Basic stochastic volatility model. +\item General non-linear model with Gaussian noise terms. +\item Model with continuous SDE dynamics. +} + +Missing values in response series are allowed as per SSM theory and can be +automatically predicted, but there can be no missing values in the system +matrices of the model. + +The package contains multiple functions for building the model: +\itemize{ +\item \code{bsm_lg} for basic univariate structural time series model (BSM), +\code{ar1} for univariate noisy AR(1) process, and \code{ssm_ulg} and \code{ssm_mlg} for +arbitrary linear gaussian model with univariate/multivariate +observations. +\item The non-Gaussian versions (where observations are non-Gaussian) of the +above models can be constructed using the functions \code{bsm_ng}, \code{ar1_ng}, +\code{ssm_ung} and \code{ssm_mng}. +\item An univariate stochastic volatility model can be defined using a function +\code{svm}. +\item For non-linear models, user must define the model using C++ snippets and +the the function \code{ssm_nlg}. See details in the \code{growth_model} vignette. +\item Diffusion models can be defined with the function \code{ssm_sde}, again using +the C++ snippets. See \code{sde_model} vignette for details. +} + +See the corresponding functions for some examples and details. + +After building the model, the model can be estimated via \code{run_mcmc} +function. The documentation of this function gives some examples. The +\code{bssm} package includes several MCMC sampling and sequential Monte +Carlo methods for models outside classic linear-Gaussian framework. For +definitions of the currently supported models and methods, usage of the +package as well as some theory behind the novel IS-MCMC and +\eqn{\psi}{psi}-APF algorithms, see Helske and Vihola (2021), Vihola, +Helske, Franks (2020), and the package vignettes. + +The output of the \code{run_mcmc} can be analysed by extracting the posterior +samples of the latent states and hyperparameters using \code{as.data.frame}, +\code{as_draws}, \code{expand_sample}, and \code{summary} methods, as well as \code{fitted} and +\code{predict} methods. Some MCMC diagnostics checks are available via +\code{check_diagnostics} function, some of which are also provided via the print +method of the \code{run_mcmc} output. Functionality of the \code{ggplot2} and +\code{bayesplot}, can be used to visualize the posterior draws or their summary +statistics, and further diagnostics checks can be performed with the help of +the \code{posterior} and \code{coda} packages. +} +\examples{ +# Create a local level model (latent random walk + noise) to the Nile +# dataset using the bsm_lg function: +model <- bsm_lg(Nile, + sd_y = tnormal(init = 100, mean = 100, sd = 100, min = 0), + sd_level = tnormal(init = 50, mean = 50, sd = 100, min = 0), + a1 = 1000, P1 = 500^2) + +# the priors for the unknown paramters sd_y and sd_level were defined +# as trunctated normal distributions, see ?bssm_prior for details + +# Run the MCMC for 2000 iterations (notice the small number of iterations to +# comply with the CRAN's check requirements) +fit <- run_mcmc(model, iter = 2000) + +# Some diagnostics checks: +check_diagnostics(fit) + +# print some summary information: +fit + +# traceplots: +plot(fit) + +# extract the summary statistics for state variable +sumr <- summary(fit,variable = "states") + +# visualize +library("ggplot2") +ggplot(sumr, aes(time, Mean)) + + geom_ribbon(aes(ymin = `2.5\%`, ymax = `97.5\%`),alpha = 0.25) + + geom_line() + + theme_bw() + } \references{ -Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +Helske J, Vihola M (2021). bssm: Bayesian Inference of Non-linear and +Non-Gaussian State Space Models in R. The R Journal (2021) 13:2, 578-589. +https://doi.org/10.32614/RJ-2021-103 + +Vihola, M, Helske, J, Franks, J. (2020). Importance sampling type estimators +based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 + +H. Wickham. ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag +New York, 2016. + +Gabry J, Mahr T (2022). “bayesplot: Plotting for Bayesian Models.” R package +version 1.9.0, https://mc-stan.org/bayesplot. + +Bürkner P, Gabry J, Kay M, Vehtari A (2022). “posterior: Tools for Working +with Posterior Distributions.” R package version 1.2.1, +https://mc-stan.org/posterior. + +Martyn Plummer, Nicky Best, Kate Cowles and Karen Vines (2006). CODA: +Convergence Diagnosis and Output Analysis for MCMC, R News, vol 6, 7-11. } diff --git a/man/bssm_prior.Rd b/man/bssm_prior.Rd new file mode 100644 index 00000000..946dc465 --- /dev/null +++ b/man/bssm_prior.Rd @@ -0,0 +1,131 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/priors.R +\name{uniform_prior} +\alias{uniform_prior} +\alias{bssm_prior} +\alias{bssm_prior_list} +\alias{uniform} +\alias{halfnormal_prior} +\alias{halfnormal} +\alias{normal_prior} +\alias{normal} +\alias{tnormal_prior} +\alias{tnormal} +\alias{gamma_prior} +\alias{gamma} +\title{Prior objects for bssm models} +\usage{ +uniform_prior(init, min, max) + +uniform(init, min, max) + +halfnormal_prior(init, sd) + +halfnormal(init, sd) + +normal_prior(init, mean, sd) + +normal(init, mean, sd) + +tnormal_prior(init, mean, sd, min = -Inf, max = Inf) + +tnormal(init, mean, sd, min = -Inf, max = Inf) + +gamma_prior(init, shape, rate) + +gamma(init, shape, rate) +} +\arguments{ +\item{init}{Initial value for the parameter, used in initializing the model +components and as a starting values in MCMC.} + +\item{min}{Lower bound of the uniform and truncated normal prior.} + +\item{max}{Upper bound of the uniform and truncated normal prior.} + +\item{sd}{Positive value defining the standard deviation of the +(underlying i.e. non-truncated) Normal distribution.} + +\item{mean}{Mean of the Normal prior.} + +\item{shape}{Positive shape parameter of the Gamma prior.} + +\item{rate}{Positive rate parameter of the Gamma prior.} +} +\value{ +object of class \code{bssm_prior} or \code{bssm_prior_list} in case +of multiple priors (i.e. multiple regression coefficients). +} +\description{ +These simple objects of class \code{bssm_prior} are used to construct a +prior distributions for the hyperparameters theta for some of the model +objects of \code{bssm} package. Note that these priors do not include the +constant terms as they do not affect the sampling. +} +\details{ +Currently supported priors are +\itemize{ +\item uniform prior (\code{uniform()}) with a probability density function (pdf) +defined as \eqn{\frac{1}{max - min}} for \eqn{min < theta < max}. +\item normal (\code{normal()}), a normal distribution parameterized via mean and +standard deviation, i.e. N(mean, sd^2). +\item truncated normal distribution (\code{tnormal()}), a normal distribution +with known truncation points (from below and/or above). Ignoring the +scaling factors, this corresponds to the pdf of N(mean, sd^2) when +\eqn{min < theta < max} and zero otherwise. +\item half-normal (\code{halfnormal()}) with a pdf matching the pdf of the +truncated normal distribution with min=0 and max=inf. +\item gamma (\code{gamma}), a gamma distribution with shape and rate +parameterization. +} + +All parameters are vectorized so for regression coefficient vector beta you +can define prior for example as \code{normal(0, 0, c(10, 20))}. + +For the general exponential models, i.e. models built with the \code{ssm_ulg}, +\code{ssm_ung}, \code{ssm_mlg}, and \code{ssm_mng}, you can define arbitrary priors by +defining the \code{prior_fn} function, which takes the one argument, \code{theta}, +corresponding to the hyperparameter vector of the model, +and returns a log-density of the (joint) prior (see the R Journal paper and +e.g. \code{ssm_ulg} for examples). Similarly, the priors for the non-linear +models (\code{ssm_nlg}) and SDE models (\code{ssm_sde}) are constructed +via C++ snippets (see the vignettes for details). + +The longer name versions of the prior functions with \code{_prior} ending +are identical with shorter versions and they are available only to +avoid clash with R's primitive function \code{gamma} (other long prior names +are just for consistent naming). +} +\examples{ + +# create uniform prior on [-1, 1] for one parameter with initial value 0.2: +uniform(init = 0.2, min = -1.0, max = 1.0) +# two normal priors at once i.e. for coefficients beta: +normal(init = c(0.1, 2.5), mean = 0.1, sd = c(1.5, 2.8)) +# Gamma prior (not run because autotest tests complain) +# gamma(init = 0.1, shape = 2.5, rate = 1.1) +# Same as +gamma_prior(init = 0.1, shape = 2.5, rate = 1.1) +# Half-normal +halfnormal(init = 0.01, sd = 0.1) +# Truncated normal +tnormal(init = 5.2, mean = 5.0, sd = 3.0, min = 0.5, max = 9.5) + + +# Further examples for diagnostic purposes: +uniform(c(0, 0.2), c(-1.0, 0.001), c(1.0, 1.2)) +normal(c(0, 0.2), c(-1.0, 0.001), c(1.0, 1.2)) +tnormal(c(2, 2.2), c(-1.0, 0.001), c(1.0, 1.2), c(1.2, 2), 3.3) +halfnormal(c(0, 0.2), c(1.0, 1.2)) +# not run because autotest bug +# gamma(c(0.1, 0.2), c(1.2, 2), c(3.3, 3.3)) + +# longer versions: +uniform_prior(init = c(0, 0.2), min = c(-1.0, 0.001), max = c(1.0, 1.2)) +normal_prior(init = c(0, 0.2), mean = c(-1.0, 0.001), sd = c(1.0, 1.2)) +tnormal_prior(init = c(2, 2.2), mean = c(-1.0, 0.001), sd = c(1.0, 1.2), + min = c(1.2, 2), max = 3.3) +halfnormal_prior(init = c(0, 0.2), sd = c(1.0, 1.2)) +gamma_prior(init = c(0.1, 0.2), shape = c(1.2, 2), rate = c(3.3, 3.3)) + +} diff --git a/man/check_diagnostics.Rd b/man/check_diagnostics.Rd new file mode 100644 index 00000000..7b9a5be6 --- /dev/null +++ b/man/check_diagnostics.Rd @@ -0,0 +1,50 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check_diagnostics.R +\name{check_diagnostics} +\alias{check_diagnostics} +\title{Quick Diagnostics Checks for \code{run_mcmc} Output} +\usage{ +check_diagnostics(x) +} +\arguments{ +\item{x}{Results object of class \code{mcmc_output} from +\code{\link{run_mcmc}}.} +} +\description{ +Prints out the acceptance rate, smallest effective sample sizes (ESS) and +largest Rhat values for a quick first check that the sampling worked. For +further checks, see e.g. \code{bayesplot} and \code{coda} packages. +} +\details{ +For methods other than IS-MCMC, the estimates are based on the improved +diagnostics from the \code{posterior} package.For IS-MCMC, these Rhat, +bulk-ESS, and tail-ESS estimates are based on the approximate posterior +which should look reasonable, otherwise the IS-correction does not make much +sense. For IS-MCMC, ESS estimates based on a weighted posterior are also +computed. +} +\examples{ +set.seed(1) +n <- 30 +phi <- 2 +rho <- 0.9 +sigma <- 0.1 +beta <- 0.5 +u <- rexp(n, 0.1) +x <- rnorm(n) +z <- y <- numeric(n) +z[1] <- rnorm(1, 0, sigma / sqrt(1 - rho^2)) +y[1] <- rnbinom(1, mu = u * exp(beta * x[1] + z[1]), size = phi) +for(i in 2:n) { + z[i] <- rnorm(1, rho * z[i - 1], sigma) + y[i] <- rnbinom(1, mu = u * exp(beta * x[i] + z[i]), size = phi) +} + +model <- ar1_ng(y, rho = uniform_prior(0.9, 0, 1), + sigma = gamma_prior(0.1, 2, 10), mu = 0., + phi = gamma_prior(2, 2, 1), distribution = "negative binomial", + xreg = x, beta = normal_prior(0.5, 0, 1), u = u) + +out <- run_mcmc(model, iter = 1000, particles = 10) +check_diagnostics(out) +} diff --git a/man/cpp_example_model.Rd b/man/cpp_example_model.Rd new file mode 100644 index 00000000..b71cdaf1 --- /dev/null +++ b/man/cpp_example_model.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cpp_example_models.R +\name{cpp_example_model} +\alias{cpp_example_model} +\title{Example C++ Codes for Non-Linear and SDE Models} +\usage{ +cpp_example_model(example, return_code = FALSE) +} +\arguments{ +\item{example}{Name of the example model. +Run \code{cpp_example_model("abc")} to get the names of possible models.} + +\item{return_code}{If TRUE, will not compile the model but only returns the +corresponding code.} +} +\value{ +Returns pointers to the C++ snippets defining the model, or in case +of \code{return_code = TRUE}, returns the example code without compiling. +} +\description{ +Example C++ Codes for Non-Linear and SDE Models +} +\examples{ +cpp_example_model("sde_poisson_OU", return_code = TRUE) + +} diff --git a/man/drownings.Rd b/man/drownings.Rd index 89fd2650..93dacc08 100644 --- a/man/drownings.Rd +++ b/man/drownings.Rd @@ -3,26 +3,29 @@ \docType{data} \name{drownings} \alias{drownings} -\title{Deaths by drowning in Finland in 1969-2014} +\title{Deaths by drowning in Finland in 1969-2019} \format{ -A time series object containing 46 observations. +A time series object containing 51 observations. } \source{ -Statistics Finland \url{https://pxnet2.stat.fi/PXWeb/pxweb/en/StatFin/}. +Statistics Finland +\url{https://stat.fi/tup/tilastotietokannat/index_en.html}. } \description{ -Dataset containing number of deaths by drowning in Finland in 1969-2014, -yearly average summer temperatures (June to August) and -corresponding population sizes (in hundreds of thousands). +Dataset containing number of deaths by drowning in Finland in 1969-2019, +corresponding population sizes (in hundreds of thousands), and +yearly average summer temperatures (June to August), based on simple +unweighted average of three weather stations: Helsinki (Southern Finland), +Jyvaskyla (Central Finland), and Sodankyla (Northern Finland). } \examples{ data("drownings") model <- bsm_ng(drownings[, "deaths"], u = drownings[, "population"], - xreg = drownings[, "summer_temp"], distribution = "poisson", + xreg = drownings[, "summer_temp"], distribution = "poisson", beta = normal(0, 0, 1), - sd_level = gamma(0.1,2, 10), sd_slope = gamma(0, 2, 10)) - -fit <- run_mcmc(model, iter = 5000, + sd_level = gamma_prior(0.1,2, 10), sd_slope = gamma_prior(0, 2, 10)) + +fit <- run_mcmc(model, iter = 5000, output_type = "summary", mcmc_type = "approx") fit ts.plot(model$y/model$u, exp(fit$alphahat[, 1]), col = 1:2) diff --git a/man/ekf.Rd b/man/ekf.Rd index 9d3e9cb6..342de6be 100644 --- a/man/ekf.Rd +++ b/man/ekf.Rd @@ -7,20 +7,50 @@ ekf(model, iekf_iter = 0) } \arguments{ -\item{model}{Model model} +\item{model}{Model of class \code{ssm_nlg}.} -\item{iekf_iter}{If \code{iekf_iter > 0}, iterated extended Kalman filter -is used with \code{iekf_iter} iterations.} +\item{iekf_iter}{Non-negative integer. The default zero corresponds to +normal EKF, whereas \code{iekf_iter > 0} corresponds to iterated EKF +with \code{iekf_iter} iterations.} } \value{ List containing the log-likelihood, one-step-ahead predictions \code{at} and filtered estimates \code{att} of states, and the corresponding variances \code{Pt} and - \code{Ptt}. +\code{Ptt}. } \description{ -Function \code{ekf} runs the (iterated) extended Kalman filter for the given -non-linear Gaussian model of class \code{ssm_nlg}, -and returns the filtered estimates and one-step-ahead predictions of the +Function \code{ekf} runs the (iterated) extended Kalman filter for the given +non-linear Gaussian model of class \code{ssm_nlg}, +and returns the filtered estimates and one-step-ahead predictions of the states \eqn{\alpha_t} given the data up to time \eqn{t}. } +\examples{ +\donttest{ # Takes a while on CRAN +set.seed(1) +mu <- -0.2 +rho <- 0.7 +sigma_y <- 0.1 +sigma_x <- 1 +x <- numeric(50) +x[1] <- rnorm(1, mu, sigma_x / sqrt(1 - rho^2)) +for(i in 2:length(x)) { + x[i] <- rnorm(1, mu * (1 - rho) + rho * x[i - 1], sigma_x) +} +y <- rnorm(50, exp(x), sigma_y) + +pntrs <- cpp_example_model("nlg_ar_exp") + +model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(mu= mu, rho = rho, + log_sigma_x = log(sigma_x), log_sigma_y = log(sigma_y)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state") + +out_ekf <- ekf(model_nlg, iekf_iter = 0) +out_iekf <- ekf(model_nlg, iekf_iter = 5) +ts.plot(cbind(x, out_ekf$att, out_iekf$att), col = 1:3) +} +} diff --git a/man/ekf_smoother.Rd b/man/ekf_smoother.Rd index 93152178..659035f6 100644 --- a/man/ekf_smoother.Rd +++ b/man/ekf_smoother.Rd @@ -2,23 +2,58 @@ % Please edit documentation in R/smoother.R \name{ekf_smoother} \alias{ekf_smoother} +\alias{ekf_fast_smoother} \title{Extended Kalman Smoothing} \usage{ ekf_smoother(model, iekf_iter = 0) + +ekf_fast_smoother(model, iekf_iter = 0) } \arguments{ -\item{model}{Model model} +\item{model}{Model of class \code{ssm_nlg}.} -\item{iekf_iter}{If \code{iekf_iter > 0}, iterated extended Kalman filter is -used with \code{iekf_iter} iterations.} +\item{iekf_iter}{Non-negative integer. The default zero corresponds to +normal EKF, whereas \code{iekf_iter > 0} corresponds to iterated EKF +with \code{iekf_iter} iterations.} } \value{ List containing the log-likelihood, -smoothed state estimates \code{alphahat}, and the corresponding variances \code{Vt} and - \code{Ptt}. +smoothed state estimates \code{alphahat}, and the corresponding variances +\code{Vt} and \code{Ptt}. } \description{ -Function \code{ekf_smoother} runs the (iterated) extended Kalman smoother for -the given non-linear Gaussian model of class \code{ssm_nlg}, -and returns the smoothed estimates of the states and the corresponding variances. +Function \code{ekf_smoother} runs the (iterated) extended Kalman smoother +for the given non-linear Gaussian model of class \code{ssm_nlg}, +and returns the smoothed estimates of the states and the corresponding +variances. Function \code{ekf_fast_smoother} computes only smoothed +estimates of the states. +} +\examples{ +\donttest{ # Takes a while on CRAN +set.seed(1) +mu <- -0.2 +rho <- 0.7 +sigma_y <- 0.1 +sigma_x <- 1 +x <- numeric(50) +x[1] <- rnorm(1, mu, sigma_x / sqrt(1 - rho^2)) +for(i in 2:length(x)) { + x[i] <- rnorm(1, mu * (1 - rho) + rho * x[i - 1], sigma_x) +} +y <- rnorm(length(x), exp(x), sigma_y) + +pntrs <- cpp_example_model("nlg_ar_exp") + +model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(mu= mu, rho = rho, + log_sigma_x = log(sigma_x), log_sigma_y = log(sigma_y)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state") + +out_ekf <- ekf_smoother(model_nlg, iekf_iter = 0) +out_iekf <- ekf_smoother(model_nlg, iekf_iter = 1) +ts.plot(cbind(x, out_ekf$alphahat, out_iekf$alphahat), col = 1:3) +} } diff --git a/man/ekpf_filter.Rd b/man/ekpf_filter.Rd index 63305ccb..65432d22 100644 --- a/man/ekpf_filter.Rd +++ b/man/ekpf_filter.Rd @@ -5,32 +5,62 @@ \alias{ekpf_filter.ssm_nlg} \title{Extended Kalman Particle Filtering} \usage{ -ekpf_filter(object, particles, ...) +ekpf_filter(model, particles, ...) \method{ekpf_filter}{ssm_nlg}( - object, + model, particles, seed = sample(.Machine$integer.max, size = 1), ... ) } \arguments{ -\item{object}{of class \code{ssm_nlg}.} +\item{model}{Model of class \code{ssm_nlg}.} -\item{particles}{Number of particles.} +\item{particles}{Number of particles as a positive integer. Suitable values +depend on the model and the data, and while larger values provide more +accurate estimates, the run time also increases with respect to the +number of particles, so it is generally a good idea to test the filter first +with a small number of particles, e.g., less than 100.} \item{...}{Ignored.} -\item{seed}{Seed for RNG.} +\item{seed}{Seed for the C++ RNG (positive integer).} } \value{ -A list containing samples, filtered estimates and the corresponding covariances, -weights, and an estimate of log-likelihood. +A list containing samples, filtered estimates and the +corresponding covariances, weights, and an estimate of log-likelihood. } \description{ -Function \code{ekpf_filter} performs a extended Kalman particle filtering with stratification -resampling, based on Van Der Merwe et al (2001). +Function \code{ekpf_filter} performs a extended Kalman particle filtering +with stratification resampling, based on Van Der Merwe et al (2001). +} +\examples{ +\donttest{ # Takes a while +set.seed(1) +n <- 50 +x <- y <- numeric(n) +y[1] <- rnorm(1, exp(x[1]), 0.1) +for(i in 1:(n-1)) { + x[i+1] <- rnorm(1, sin(x[i]), 0.1) + y[i+1] <- rnorm(1, exp(x[i+1]), 0.1) +} + +pntrs <- cpp_example_model("nlg_sin_exp") + +model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(log_H = log(0.1), log_R = log(0.1)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state") + +out <- ekpf_filter(model_nlg, particles = 100) +ts.plot(cbind(x, out$at[1:n], out$att[1:n]), col = 1:3) +} } \references{ -Van Der Merwe, R., Doucet, A., De Freitas, N., & Wan, E. A. (2001). The unscented particle filter. In Advances in neural information processing systems (pp. 584-590). +Van Der Merwe, R., Doucet, A., De Freitas, N., & Wan, E. A. +(2001). The unscented particle filter. In Advances in neural +information processing systems (pp. 584-590). } diff --git a/man/estimate_ess.Rd b/man/estimate_ess.Rd new file mode 100644 index 00000000..16d1f6df --- /dev/null +++ b/man/estimate_ess.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/asymptotic_var.R +\name{estimate_ess} +\alias{estimate_ess} +\title{Effective Sample Size for IS-type Estimators} +\usage{ +estimate_ess(x, w, method = "sokal") +} +\arguments{ +\item{x}{A numeric vector of samples.} + +\item{w}{A numeric vector of weights. If missing, set to 1 (i.e. no +weighting is assumed).} + +\item{method}{Method for computing the ESS. Default is \code{"sokal"}, other +option are \code{"geyer"} (see also \code{asymptotic_var}).} +} +\value{ +A single numeric value of effective sample size estimate. +} +\description{ +Computes the effective sample size (ESS) based on weighted posterior +samples. +} +\details{ +The asymptotic variance MCMCSE^2 is based on Corollary 1 of +Vihola et al. (2020) which is used to compute an estimate for the ESS +using the identity ESS(x) = var(x) / MCMCSE^2 where var(x) is the +posterior variance of x assuming independent samples. +} +\examples{ +set.seed(1) +n <- 1e4 +x <- numeric(n) +phi <- 0.7 +for(t in 2:n) x[t] <- phi * x[t-1] + rnorm(1) +w <- rexp(n, 0.5 * exp(0.001 * x^2)) +# different methods: +estimate_ess(x, w, method = "sokal") +estimate_ess(x, w, method = "geyer") + +} +\references{ +Vihola, M, Helske, J, Franks, J. (2020). Importance sampling type estimators +based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 + +Sokal A. (1997). Monte Carlo Methods in Statistical Mechanics: Foundations +and New Algorithms. +In: DeWitt-Morette C, Cartier P, Folacci A (eds) Functional Integration. +NATO ASI Series (Series B: Physics), vol 361. Springer, Boston, MA. +https://doi.org/10.1007/978-1-4899-0319-8_6 + +Gelman, A, Carlin J B, Stern H S, Dunson, D B, Vehtari A, Rubin D B. (2013). +Bayesian Data Analysis, Third Edition. Chapman and Hall/CRC. +} diff --git a/man/exchange.Rd b/man/exchange.Rd index f00bd32d..87f1e1d5 100644 --- a/man/exchange.Rd +++ b/man/exchange.Rd @@ -8,21 +8,26 @@ A vector of length 945. } \source{ -\url{http://www.ssfpack.com/DKbook.html}. +The data used to be available on the www.ssfpack.com/DKbook.html but +this page is does not seem to be available anymore. } \description{ -Dataset containing daily log-returns from 1/10/81-28/6/85 as in [1] +Dataset containing daily log-returns from 1/10/81-28/6/85 as in Durbin and +Koopman (2012). } \examples{ +\donttest{ # Don't test on CRAN as complains about parallelisation data("exchange") model <- svm(exchange, rho = uniform(0.97,-0.999,0.999), sd_ar = halfnormal(0.175, 2), mu = normal(-0.87, 0, 2)) out <- particle_smoother(model, particles = 500) -plot.ts(cbind(model$y, exp(out$alphahat))) +plot.ts(cbind(model$y, exp(out$alphahat))) +} } \references{ -James Durbin, Siem Jan Koopman (2012). "Time Series Analysis by State Space Methods". -Oxford University Press. +James Durbin, Siem Jan Koopman (2012). +Time Series Analysis by State Space Methods. Oxford University Press. +https://doi.org/10.1093/acprof:oso/9780199641178.001.0001 } \keyword{datasets} diff --git a/man/expand_sample.Rd b/man/expand_sample.Rd index 978f044a..eb86a482 100644 --- a/man/expand_sample.Rd +++ b/man/expand_sample.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/print_mcmc.R +% Please edit documentation in R/expand_sample.R \name{expand_sample} \alias{expand_sample} \title{Expand the Jump Chain representation} @@ -11,17 +11,48 @@ expand_sample(x, variable = "theta", times, states, by_states = TRUE) \item{variable}{Expand parameters \code{"theta"} or states \code{"states"}.} -\item{times}{Vector of indices. In case of states, what time points to expand? Default is all.} +\item{times}{A vector of indices. In case of states, +what time points to expand? Default is all.} -\item{states}{Vector of indices. In case of states, what states to expand? Default is all.} +\item{states}{A vector of indices. In case of states, +what states to expand? Default is all.} -\item{by_states}{If \code{TRUE} (default), return list by states. Otherwise by time.} +\item{by_states}{If \code{TRUE} (default), return list by states. +Otherwise by time.} +} +\value{ +An object of class \code{"mcmc"} of the \code{coda} package. } \description{ -The MCMC algorithms of \code{bssm} use a jump chain representation where we -store the accepted values and the number of times we stayed in the current value. -Although this saves bit memory and is especially convenient for IS-corrected -MCMC, sometimes we want to have the usual sample paths. Function \code{expand_sample} -returns the expanded sample based on the counts. Note that for IS-corrected output the expanded -sample corresponds to the approximate posterior. +The MCMC algorithms of \code{bssm} use a jump chain representation where we +store the accepted values and the number of times we stayed in the current +value. Although this saves bit memory and is especially convenient for +IS-corrected MCMC, sometimes we want to have the usual sample paths +(for example for drawing traceplots). +Function \code{expand_sample} returns the expanded sample based on the +counts (in form of \code{coda::mcmc} object. Note that for +the IS-MCMC the expanded sample corresponds to the approximate posterior, +i.e., the weights are ignored. +} +\details{ +This functions is mostly for backwards compatibility, methods +\code{as.data.frame} and \code{as_draws} produce likely more convenient +output. +} +\examples{ +set.seed(1) +n <- 50 +x <- cumsum(rnorm(n)) +y <- rnorm(n, x) +model <- bsm_lg(y, sd_y = gamma_prior(1, 2, 2), + sd_level = gamma_prior(1, 2, 2)) +fit <- run_mcmc(model, iter = 1e4) +# Traceplots for theta +plot.ts(expand_sample(fit, variable = "theta")) +# Traceplot for x_5 +plot.ts(expand_sample(fit, variable = "states", times = 5, + states = 1)$level) +} +\seealso{ +\code{as.data.frame.mcmc_output} and \code{as_draws.mcmc_output}. } diff --git a/man/figures/README-bivariate-fig-1.png b/man/figures/README-bivariate-fig-1.png new file mode 100644 index 00000000..a87efe8c Binary files /dev/null and b/man/figures/README-bivariate-fig-1.png differ diff --git a/man/figures/README-bivariate-fig-2.png b/man/figures/README-bivariate-fig-2.png new file mode 100644 index 00000000..4355d093 Binary files /dev/null and b/man/figures/README-bivariate-fig-2.png differ diff --git a/man/figures/README-compare-1.png b/man/figures/README-compare-1.png new file mode 100644 index 00000000..cb8797e8 Binary files /dev/null and b/man/figures/README-compare-1.png differ diff --git a/man/figures/README-example-1.png b/man/figures/README-example-1.png new file mode 100644 index 00000000..69a41e40 Binary files /dev/null and b/man/figures/README-example-1.png differ diff --git a/man/fitted.mcmc_output.Rd b/man/fitted.mcmc_output.Rd new file mode 100644 index 00000000..b63fafbd --- /dev/null +++ b/man/fitted.mcmc_output.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fitted.R +\name{fitted.mcmc_output} +\alias{fitted.mcmc_output} +\title{Fitted for State Space Model} +\usage{ +\method{fitted}{mcmc_output}(object, model, probs = c(0.025, 0.975), ...) +} +\arguments{ +\item{object}{Results object of class \code{mcmc_output} from +\code{\link{run_mcmc}} based on the input model.} + +\item{model}{A \code{bssm_model} object.} + +\item{probs}{Numeric vector defining the quantiles of interest. Default is +\code{c(0.025, 0.975)}.} + +\item{...}{Ignored.} +} +\description{ +Returns summary statistics from the posterior predictive +distribution of the mean. +} +\examples{ +prior <- uniform(0.1 * sd(log10(UKgas)), 0, 1) +model <- bsm_lg(log10(UKgas), sd_y = prior, sd_level = prior, + sd_slope = prior, sd_seasonal = prior, period = 4) +fit <- run_mcmc(model, iter = 1e4) +res <- fitted(fit, model) +head(res) + +} diff --git a/man/gaussian_approx.Rd b/man/gaussian_approx.Rd index 8d052189..88325b77 100644 --- a/man/gaussian_approx.Rd +++ b/man/gaussian_approx.Rd @@ -13,28 +13,50 @@ gaussian_approx(model, max_iter, conv_tol, ...) \method{gaussian_approx}{ssm_nlg}(model, max_iter = 100, conv_tol = 1e-08, iekf_iter = 0, ...) } \arguments{ -\item{model}{Model to be approximated.} +\item{model}{Model to be approximated. Should be of class +\code{bsm_ng}, \code{ar1_ng} \code{svm}, +\code{ssm_ung}, or \code{ssm_mng}, or \code{ssm_nlg}, i.e. non-gaussian or +non-linear \code{bssm_model}.} -\item{max_iter}{Maximum number of iterations.} +\item{max_iter}{Maximum number of iterations as a positive integer. +Default is 100 (although typically only few iterations are needed).} -\item{conv_tol}{Tolerance parameter.} +\item{conv_tol}{Positive tolerance parameter. Default is 1e-8. Approximation +is claimed to be converged when the mean squared difference of the modes of +is less than \code{conv_tol}.} \item{...}{Ignored.} -\item{iekf_iter}{For non-linear models, number of iterations in iterated EKF (defaults to 0).} +\item{iekf_iter}{For non-linear models, non-negative number of iterations in +iterated EKF (defaults to 0, i.e. normal EKF). Used only for models of class +\code{ssm_nlg}.} +} +\value{ +Returns linear-Gaussian SSM of class \code{ssm_ulg} or +\code{ssm_mlg} which has the same conditional mode of p(alpha|y, theta) as +the original model. } \description{ -Returns the approximating Gaussian model. This function is rarely needed itself, -and is mainly available for testing and debugging purposes. +Returns the approximating Gaussian model which has the same conditional +mode of p(alpha|y, theta) as the original model. +This function is rarely needed itself, and is mainly available for +testing and debugging purposes. } \examples{ data("poisson_series") model <- bsm_ng(y = poisson_series, sd_slope = 0.01, sd_level = 0.1, distribution = "poisson") out <- gaussian_approx(model) +for(i in 1:7) + cat("Number of iterations used: ", i, ", y[1] = ", + gaussian_approx(model, max_iter = i, conv_tol = 0)$y[1], "\n", sep ="") + } \references{ -Koopman, S.J. and Durbin J. (2012). Time Series Analysis by State Space Methods. Second edition. Oxford: Oxford University Press. -Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +Koopman, SJ and Durbin J (2012). Time Series Analysis by State Space +Methods. Second edition. Oxford: Oxford University Press. + +Vihola, M, Helske, J, Franks, J. (2020). Importance sampling type estimators +based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 } diff --git a/man/iact.Rd b/man/iact.Rd new file mode 100644 index 00000000..79d40b2a --- /dev/null +++ b/man/iact.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/asymptotic_var.R +\name{iact} +\alias{iact} +\title{Integrated Autocorrelation Time} +\usage{ +iact(x) +} +\arguments{ +\item{x}{A numeric vector.} +} +\value{ +A single numeric value of IACT estimate. +} +\description{ +Estimates the integrated autocorrelation time (IACT) based on Sokal (1997). +Note that the estimator is not particularly good for very short series x +(say < 100), but that is not very practical for MCMC applications anyway. +} +\examples{ +set.seed(1) +n <- 1000 +x <- numeric(n) +phi <- 0.8 +for(t in 2:n) x[t] <- phi * x[t-1] + rnorm(1) +iact(x) +} +\references{ +Sokal A. (1997) Monte Carlo Methods in Statistical Mechanics: Foundations +and New Algorithms. +In: DeWitt-Morette C., Cartier P., Folacci A. (eds) Functional Integration. +NATO ASI Series (Series B: Physics), vol 361. Springer, Boston, MA. +https://doi.org/10.1007/978-1-4899-0319-8_6 +} diff --git a/man/importance_sample.Rd b/man/importance_sample.Rd index 89b7933f..9a2dac64 100644 --- a/man/importance_sample.Rd +++ b/man/importance_sample.Rd @@ -18,38 +18,47 @@ importance_sample(model, nsim, use_antithetic, max_iter, conv_tol, seed, ...) ) } \arguments{ -\item{model}{of class \code{bsm_ng}, \code{ar1_ng} \code{svm}, \code{ssm_ung}, or \code{ssm_mng}.} +\item{model}{Model of class \code{bsm_ng}, \code{ar1_ng} \code{svm}, +\code{ssm_ung}, or \code{ssm_mng}.} -\item{nsim}{Number of samples.} +\item{nsim}{Number of samples (positive integer). Suitable values +depend on the model and the data, and while larger values provide more +accurate estimates, the run time also increases with respect to to the +number of samples, so it is generally a good idea to test the filter first +with a small number of samples, e.g., less than 100.} -\item{use_antithetic}{Logical. If \code{TRUE} (default), use antithetic -variable for location in simulation smoothing. Ignored for \code{ssm_mng} models.} +\item{use_antithetic}{Logical. If \code{TRUE} (default), use antithetic +variable for location in simulation smoothing. Ignored for \code{ssm_mng} +models.} -\item{max_iter}{Maximum number of iterations used for the approximation.} +\item{max_iter}{Maximum number of iterations as a positive integer. +Default is 100 (although typically only few iterations are needed).} -\item{conv_tol}{Convergence threshold for the approximation. Approximation is -claimed to be converged when the mean squared difference of the modes is -less than \code{conv_tol}.} +\item{conv_tol}{Positive tolerance parameter. Default is 1e-8. Approximation +is claimed to be converged when the mean squared difference of the modes of +is less than \code{conv_tol}.} -\item{seed}{Seed for the random number generator.} +\item{seed}{Seed for the C++ RNG (positive integer).} \item{...}{Ignored.} } \description{ -Returns \code{nsim} samples from the approximating Gaussian model with corresponding -(scaled) importance weights. Probably mostly useful for comparing KFAS and bssm packages. +Returns \code{nsim} samples from the approximating Gaussian model with +corresponding (scaled) importance weights. +Probably mostly useful for comparing KFAS and bssm packages. } \examples{ data("sexratio", package = "KFAS") -model <- bsm_ng(sexratio[, "Male"], sd_level = 0.001, u = sexratio[, "Total"], +model <- bsm_ng(sexratio[, "Male"], sd_level = 0.001, + u = sexratio[, "Total"], distribution = "binomial") imp <- importance_sample(model, nsim = 1000) est <- matrix(NA, 3, nrow(sexratio)) for(i in 1:ncol(est)) { - est[, i] <- Hmisc::wtd.quantile(exp(imp$alpha[i, 1, ]), imp$weights, - prob = c(0.05,0.5,0.95), normwt=TRUE) + est[, i] <- diagis::weighted_quantile(exp(imp$alpha[i, 1, ]), imp$weights, + prob = c(0.05,0.5,0.95)) } ts.plot(t(est),lty = c(2,1,2)) diff --git a/man/kfilter.Rd b/man/kfilter.Rd index 46b60135..edd849c0 100644 --- a/man/kfilter.Rd +++ b/man/kfilter.Rd @@ -2,34 +2,37 @@ % Please edit documentation in R/kfilter.R \name{kfilter} \alias{kfilter} -\alias{kfilter.gaussian} +\alias{kfilter.lineargaussian} \alias{kfilter.nongaussian} \title{Kalman Filtering} \usage{ kfilter(model, ...) -\method{kfilter}{gaussian}(model, ...) +\method{kfilter}{lineargaussian}(model, ...) \method{kfilter}{nongaussian}(model, ...) } \arguments{ -\item{model}{Model Model object.} +\item{model}{Model of class \code{lineargaussian}, \code{nongaussian} or +\code{ssm_nlg}.} \item{...}{Ignored.} } \value{ -List containing the log-likelihood (approximate in non-Gaussian case), -one-step-ahead predictions \code{at} and filtered -estimates \code{att} of states, and the corresponding variances \code{Pt} and - \code{Ptt}. +List containing the log-likelihood +(approximate in non-Gaussian case), one-step-ahead predictions \code{at} +and filtered estimates \code{att} of states, and the corresponding +variances \code{Pt} and \code{Ptt} up to the time point n+1 where n is the +length of the input time series. } \description{ -Function \code{kfilter} runs the Kalman filter for the given model, -and returns the filtered estimates and one-step-ahead predictions of the +Function \code{kfilter} runs the Kalman filter for the given model, +and returns the filtered estimates and one-step-ahead predictions of the states \eqn{\alpha_t} given the data up to time \eqn{t}. } \details{ -For non-Gaussian models, the filtering is based on the approximate Gaussian model. +For non-Gaussian models, the filtering is based on the approximate +Gaussian model. } \examples{ x <- cumsum(rnorm(20)) diff --git a/man/logLik.Rd b/man/logLik.Rd deleted file mode 100644 index 102459a4..00000000 --- a/man/logLik.Rd +++ /dev/null @@ -1,20 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/loglik.R -\name{logLik.gaussian} -\alias{logLik.gaussian} -\title{Log-likelihood of a Gaussian State Space Model} -\usage{ -\method{logLik}{gaussian}(object, ...) -} -\arguments{ -\item{object}{Model model.} - -\item{...}{Ignored.} -} -\description{ -Computes the log-likelihood of a linear-Gaussian state space model of \code{bssm} package. -} -\examples{ -model <- ssm_ulg(y = c(1,4,3), Z = 1, H = 1, T = 1, R = 1) -logLik(model) -} diff --git a/man/logLik.nongaussian.Rd b/man/logLik.nongaussian.Rd deleted file mode 100644 index cae97503..00000000 --- a/man/logLik.nongaussian.Rd +++ /dev/null @@ -1,49 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/loglik.R -\name{logLik.nongaussian} -\alias{logLik.nongaussian} -\title{Log-likelihood of a Non-Gaussian State Space Model} -\usage{ -\method{logLik}{nongaussian}( - object, - particles, - method = "psi", - max_iter = 100, - conv_tol = 1e-08, - seed = sample(.Machine$integer.max, size = 1), - ... -) -} -\arguments{ -\item{object}{Model model.} - -\item{particles}{Number of samples for particle filter or importance sampling. If 0, -approximate log-likelihood based on the Gaussian approximation is returned.} - -\item{method}{Sampling method, default is psi-auxiliary filter (\code{"psi"}), -other choices are \code{"bsf"} bootstrap particle filter, and \code{"spdk"}, -which uses the importance sampling approach by Shephard and Pitt (1997) and -Durbin and Koopman (1997).} - -\item{max_iter}{Maximum number of iterations for Gaussian approximation algorithm.} - -\item{conv_tol}{Tolerance parameter for the approximation algorithm.} - -\item{seed}{Seed for the random number generator.} - -\item{...}{Ignored.} -} -\description{ -Computes the log-likelihood of a non-Gaussian state space model of \code{bssm} package. -} -\examples{ -model <- ssm_ung(y = c(1,4,3), Z = 1, T = 1, R = 0.5, P1 = 2, - distribution = "poisson") - -model2 <- bsm_ng(y = c(1,4,3), sd_level = 0.5, P1 = 2, - distribution = "poisson") -logLik(model, particles = 0) -logLik(model2, particles = 0) -logLik(model, particles = 10, seed = 1) -logLik(model2, particles = 10, seed = 1) -} diff --git a/man/logLik.ssm_nlg.Rd b/man/logLik.ssm_nlg.Rd deleted file mode 100644 index fbdc3d62..00000000 --- a/man/logLik.ssm_nlg.Rd +++ /dev/null @@ -1,43 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/loglik.R -\name{logLik.ssm_nlg} -\alias{logLik.ssm_nlg} -\title{Log-likelihood of a Non-linear State Space Model} -\usage{ -\method{logLik}{ssm_nlg}( - object, - particles, - method = "bsf", - max_iter = 100, - conv_tol = 1e-08, - iekf_iter = 0, - seed = sample(.Machine$integer.max, size = 1), - ... -) -} -\arguments{ -\item{object}{Model model.} - -\item{particles}{Number of samples for particle filter. If 0, -approximate log-likelihood is returned either based on the Gaussian approximation or EKF, -depending on the \code{method} argument.} - -\item{method}{Sampling method. Default is the bootstrap particle filter (\code{"bsf"}). -Other choices are \code{"psi"} which uses psi-auxiliary filter -(or approximating Gaussian model in the case of \code{particles = 0}), and \code{"ekf"} which -uses EKF-based particle filter (or just EKF approximation in the case of \code{particles = 0}).} - -\item{max_iter}{Maximum number of iterations for gaussian approximation algorithm.} - -\item{conv_tol}{Tolerance parameter for the approximation algorithm.} - -\item{iekf_iter}{If \code{iekf_iter > 0}, iterated extended Kalman filter is used with -\code{iekf_iter} iterations in place of standard EKF. Defaults to zero.} - -\item{seed}{Seed for the random number generator.} - -\item{...}{Ignored.} -} -\description{ -Computes the log-likelihood of a state space model of class \code{ssm_nlg} package. -} diff --git a/man/logLik.ssm_sde.Rd b/man/logLik.ssm_sde.Rd deleted file mode 100644 index 529f863e..00000000 --- a/man/logLik.ssm_sde.Rd +++ /dev/null @@ -1,28 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/loglik.R -\name{logLik.ssm_sde} -\alias{logLik.ssm_sde} -\title{Log-likelihood of a State Space Model with SDE dynamics} -\usage{ -\method{logLik}{ssm_sde}( - object, - particles, - L, - seed = sample(.Machine$integer.max, size = 1), - ... -) -} -\arguments{ -\item{object}{Model model.} - -\item{particles}{Number of samples for particle filter.} - -\item{L}{Integer defining the discretization level defined as (2^L).} - -\item{seed}{Seed for the random number generator.} - -\item{...}{Ignored.} -} -\description{ -Computes the log-likelihood of a state space model of class \code{ssm_sde} package. -} diff --git a/man/logLik_bssm.Rd b/man/logLik_bssm.Rd new file mode 100644 index 00000000..ca38a076 --- /dev/null +++ b/man/logLik_bssm.Rd @@ -0,0 +1,124 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/loglik.R +\name{logLik.lineargaussian} +\alias{logLik.lineargaussian} +\alias{logLik.nongaussian} +\alias{logLik.ssm_nlg} +\alias{logLik.ssm_sde} +\title{Extract Log-likelihood of a State Space Model of class \code{bssm_model}} +\usage{ +\method{logLik}{lineargaussian}(object, ...) + +\method{logLik}{nongaussian}( + object, + particles, + method = "psi", + max_iter = 100, + conv_tol = 1e-08, + seed = sample(.Machine$integer.max, size = 1), + ... +) + +\method{logLik}{ssm_nlg}( + object, + particles, + method = "bsf", + max_iter = 100, + conv_tol = 1e-08, + iekf_iter = 0, + seed = sample(.Machine$integer.max, size = 1), + ... +) + +\method{logLik}{ssm_sde}( + object, + particles, + L, + seed = sample(.Machine$integer.max, size = 1), + ... +) +} +\arguments{ +\item{object}{Model of class \code{bssm_model}.} + +\item{...}{Ignored.} + +\item{particles}{Number of samples for particle filter +(non-negative integer). If 0, approximate log-likelihood is returned either +based on the Gaussian approximation or EKF, depending on the \code{method} +argument.} + +\item{method}{Sampling method. For Gaussian and non-Gaussian models with +linear dynamics,options are \code{"bsf"} (bootstrap particle filter, default +for non-linear models) and \code{"psi"} (\eqn{\psi}-APF, the default for +other models). For-nonlinear models option \code{"ekf"} +uses EKF/IEKF-based particle filter (or just EKF/IEKF approximation in the +case of \code{particles = 0}).} + +\item{max_iter}{Maximum number of iterations used in Gaussian approximation, +as a positive integer. +Default is 100 (although typically only few iterations are needed).} + +\item{conv_tol}{Positive tolerance parameter used in Gaussian approximation. +Default is 1e-8.} + +\item{seed}{Seed for the C++ RNG (positive integer).} + +\item{iekf_iter}{Non-negative integer. If zero (default), first +approximation for non-linear Gaussian models is obtained from extended +Kalman filter. If \code{iekf_iter > 0}, iterated extended Kalman filter is +used with \code{iekf_iter} iterations.} + +\item{L}{Integer defining the discretization level defined as (2^L).} +} +\value{ +A numeric value. +} +\description{ +Computes the log-likelihood of a state space model defined by \code{bssm} +package. +} +\examples{ + +model <- ssm_ulg(y = c(1,4,3), Z = 1, H = 1, T = 1, R = 1) +logLik(model) +model <- ssm_ung(y = c(1,4,3), Z = 1, T = 1, R = 0.5, P1 = 2, + distribution = "poisson") + +model2 <- bsm_ng(y = c(1,4,3), sd_level = 0.5, P1 = 2, + distribution = "poisson") + +logLik(model, particles = 0) +logLik(model2, particles = 0) +logLik(model, particles = 10, seed = 1) +logLik(model2, particles = 10, seed = 1) +} +\references{ +Durbin, J., & Koopman, S. (2002). A Simple and Efficient Simulation +Smoother for State Space Time Series Analysis. Biometrika, 89(3), 603-615. + +Shephard, N., & Pitt, M. (1997). Likelihood Analysis of +Non-Gaussian Measurement Time Series. Biometrika, 84(3), 653-667. + +Gordon, NJ, Salmond, DJ, Smith, AFM (1993). +Novel approach to nonlinear/non-Gaussian Bayesian state estimation. +IEE Proceedings-F, 140, 107-113. + +Vihola, M, Helske, J, Franks, J. Importance sampling type estimators +based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 2020; 1-38. https://doi.org/10.1111/sjos.12492 + +Van Der Merwe, R, Doucet, A, De Freitas, N, Wan, EA (2001). +The unscented particle filter. +In Advances in neural information processing systems, p 584-590. + +Jazwinski, A 1970. Stochastic Processes and Filtering Theory. +Academic Press. + +Kitagawa, G (1996). Monte Carlo filter and smoother for non-Gaussian +nonlinear state space models. +Journal of Computational and Graphical Statistics, 5, 1-25. +} +\seealso{ +particle_smoother +} diff --git a/man/negbin_model.Rd b/man/negbin_model.Rd new file mode 100644 index 00000000..b95a0928 --- /dev/null +++ b/man/negbin_model.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/bssm-package.R +\docType{data} +\name{negbin_model} +\alias{negbin_model} +\title{Estimated Negative Binomial Model of Helske and Vihola (2021)} +\format{ +A object of class \code{mcmc_output}. +} +\description{ +This model was used in Helske and Vihola (2021), but with larger number of +iterations. Here only 2000 iterations were used in order to reduce the size +of the model object in CRAN. +} +\examples{ +# reproducing the model: +data("negbin_series") +# Construct model for bssm +bssm_model <- bsm_ng(negbin_series[, "y"], + xreg = negbin_series[, "x"], + beta = normal(0, 0, 10), + phi = halfnormal(1, 10), + sd_level = halfnormal(0.1, 1), + sd_slope = halfnormal(0.01, 0.1), + a1 = c(0, 0), P1 = diag(c(10, 0.1)^2), + distribution = "negative binomial") + +\donttest{ +# In the paper we used 60000 iterations with first 10000 as burnin +fit_bssm <- run_mcmc(bssm_model, iter = 2000, particles = 10, seed = 1) +fit_bssm +} +} +\references{ +Helske J, Vihola M (2021). bssm: Bayesian Inference of Non-linear and +Non-Gaussian State Space Models in R. The R Journal (2021) 13:2, 578-589. +https://doi.org/10.32614/RJ-2021-103 +} +\keyword{datasets} diff --git a/man/negbin_series.Rd b/man/negbin_series.Rd new file mode 100644 index 00000000..c49f50ec --- /dev/null +++ b/man/negbin_series.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/bssm-package.R +\docType{data} +\name{negbin_series} +\alias{negbin_series} +\title{Simulated Negative Binomial Time Series Data} +\format{ +A time series \code{mts} object with 200 time points and two series. +} +\description{ +See example for code for reproducing the data. This was used in +Helske and Vihola (2021). +} +\examples{ +# The data was generated as follows: +set.seed(123) +n <- 200 +sd_level <- 0.1 +drift <- 0.01 +beta <- -0.9 +phi <- 5 + +level <- cumsum(c(5, drift + rnorm(n - 1, sd = sd_level))) +x <- 3 + (1:n) * drift + sin(1:n + runif(n, -1, 1)) +y <- rnbinom(n, size = phi, mu = exp(beta * x + level)) + +} +\references{ +Helske J, Vihola M (2021). bssm: Bayesian Inference of Non-linear and +Non-Gaussian State Space Models in R. The R Journal (2021) 13:2, 578-589. +https://doi.org/10.32614/RJ-2021-103 +} +\seealso{ +\code{negbin_model} +} +\keyword{datasets} diff --git a/man/particle_smoother.Rd b/man/particle_smoother.Rd index ab78fc11..7b6ef671 100644 --- a/man/particle_smoother.Rd +++ b/man/particle_smoother.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/particle_smoother.R \name{particle_smoother} \alias{particle_smoother} -\alias{particle_smoother.gaussian} +\alias{particle_smoother.lineargaussian} \alias{particle_smoother.nongaussian} \alias{particle_smoother.ssm_nlg} \alias{particle_smoother.ssm_sde} @@ -10,7 +10,7 @@ \usage{ particle_smoother(model, particles, ...) -\method{particle_smoother}{gaussian}( +\method{particle_smoother}{lineargaussian}( model, particles, method = "psi", @@ -48,42 +48,55 @@ particle_smoother(model, particles, ...) ) } \arguments{ -\item{model}{Model.} +\item{model}{A model object of class \code{bssm_model}.} -\item{particles}{Number of samples for particle filter.} +\item{particles}{Number of particles as a positive integer. Suitable values +depend on the model, the data, and the chosen algorithm. While larger values +provide more accurate estimates, the run time also increases with respect to +the number of particles, so it is generally a good idea to test the filter +first with a small number of particles, e.g., less than 100.} \item{...}{Ignored.} -\item{method}{Choice of particle filter algorithm. +\item{method}{Choice of particle filter algorithm. For Gaussian and non-Gaussian models with linear dynamics, -options are \code{"bsf"} (bootstrap particle filter, default for non-linear models) -and \code{"psi"} (\eqn{\psi}-APF, the default for other models), and -for non-linear models options \code{"ekf"} (extended Kalman particle filter) +options are \code{"bsf"} (bootstrap particle filter, default for +non-linear models) +and \code{"psi"} (\eqn{\psi}-APF, the default for other models), and +for non-linear models option \code{"ekf"} (extended Kalman particle filter) is also available.} -\item{seed}{Seed for RNG.} +\item{seed}{Seed for the C++ RNG (positive integer).} -\item{max_iter}{Maximum number of iterations used in Gaussian approximation. Used \eqn{\psi}-APF.} +\item{max_iter}{Maximum number of iterations used in Gaussian approximation, +as a positive integer. +Default is 100 (although typically only few iterations are needed).} -\item{conv_tol}{Tolerance parameter used in Gaussian approximation. Used \eqn{\psi}-APF.} +\item{conv_tol}{Positive tolerance parameter used in Gaussian approximation. +Default is 1e-8.} -\item{iekf_iter}{If zero (default), first approximation for non-linear -Gaussian models is obtained from extended Kalman filter. If -\code{iekf_iter > 0}, iterated extended Kalman filter is used with -\code{iekf_iter} iterations.} +\item{iekf_iter}{Non-negative integer. If zero (default), first +approximation for non-linear Gaussian models is obtained from extended +Kalman filter. If \code{iekf_iter > 0}, iterated extended Kalman filter is +used with \code{iekf_iter} iterations.} -\item{L}{Integer defining the discretization level.} +\item{L}{Positive integer defining the discretization level for SDE model.} } \value{ -List with samples (\code{alpha}) from the smoothing distribution and corresponding weights (\code{weights}), - as well as smoothed means and covariances (\code{alphahat} and \code{Vt}) of the states and - estimated log-likelihood (\code{logLik}). +List with samples (\code{alpha}) from the smoothing distribution +and corresponding weights (\code{weights}), +as well as smoothed means and covariances (\code{alphahat} and \code{Vt}) +of the states and +estimated log-likelihood (\code{logLik}). } \description{ -Function \code{particle_smoother} performs particle smoothing -based on either bootstrap particle filter [1], \eqn{\psi}-auxiliary particle filter (\eqn{\psi}-APF) [2], -or extended Kalman particle filter [3] (or its iterated version [4]). -The smoothing phase is based on the filter-smoother algorithm by [5]. +Function \code{particle_smoother} performs particle smoothing +based on either bootstrap particle filter (Gordon et al. 1993), +\eqn{\psi}-auxiliary particle filter (\eqn{\psi}-APF) (Vihola et al. 2020), +extended Kalman particle filter (Van Der Merwe et al. 2001), +or its version based on iterated EKF (Jazwinski, 1970). +The smoothing phase is based on the filter-smoother algorithm by +Kitagawa (1996). } \details{ See one of the vignettes for \eqn{\psi}-APF in case of nonlinear models. @@ -95,18 +108,31 @@ y <- rnorm(100, x) model <- ssm_ulg(y, Z = 1, T = 1, R = 1, H = 1, P1 = 1) system.time(out <- particle_smoother(model, particles = 1000)) # same with simulation smoother: -system.time(out2 <- sim_smoother(model, particles = 1000, use_antithetic = TRUE)) +system.time(out2 <- sim_smoother(model, particles = 1000, + use_antithetic = TRUE)) ts.plot(out$alphahat, rowMeans(out2), col = 1:2) } \references{ -[1] Gordon, N. J., Salmond, D. J., & Smith, A. F. M. (1993). -Novel approach to nonlinear/non-Gaussian Bayesian state estimation. IEE Proceedings-F, 140, 107–113. -[2] Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 -[3] Van Der Merwe, R., Doucet, A., De Freitas, N., & Wan, E. A. (2001). The unscented particle filter. -In Advances in neural information processing systems (pp. 584-590). -[4] Jazwinski, A. 1970. Stochastic Processes and Filtering Theory. Academic Press. -[5] Kitagawa, G. (1996). Monte Carlo filter and smoother for non-Gaussian nonlinear state space models. -Journal of Computational and Graphical Statistics, 5, 1–25. +Gordon, NJ, Salmond, DJ, Smith, AFM (1993). +Novel approach to nonlinear/non-Gaussian Bayesian state estimation. +IEE Proceedings-F, 140, 107-113. +https://doi.org/10.1049/ip-f-2.1993.0015 + +Vihola, M, Helske, J, Franks, J. Importance sampling type estimators +based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 2020; 1-38. +https://doi.org/10.1111/sjos.12492 + +Van Der Merwe, R, Doucet, A, De Freitas, N, Wan, EA (2001). +The unscented particle filter. +In Advances in neural information processing systems, p 584-590. + +Jazwinski, A 1970. Stochastic Processes and Filtering Theory. +Academic Press. + +Kitagawa, G (1996). Monte Carlo filter and smoother for non-Gaussian +nonlinear state space models. +Journal of Computational and Graphical Statistics, 5, 1-25. +https://doi.org/10.2307/1390750 } diff --git a/man/plot.mcmc_output.Rd b/man/plot.mcmc_output.Rd new file mode 100644 index 00000000..b6ee2d38 --- /dev/null +++ b/man/plot.mcmc_output.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/plot_mcmc.R +\name{plot.mcmc_output} +\alias{plot.mcmc_output} +\title{Trace and Density Plots for \code{mcmc_output}} +\usage{ +\method{plot}{mcmc_output}(x, ...) +} +\arguments{ +\item{x}{Object of class \code{mcmc_output} from \code{\link{run_mcmc}}.} + +\item{...}{Further arguments to \link[bayesplot:MCMC-combos]{bayesplot::mcmc_combo}.} +} +\value{ +The output object from \link[bayesplot:MCMC-combos]{bayesplot::mcmc_combo}. +} +\description{ +Plots the trace and density plots of the hyperparameters theta from the MCMC +run by \code{\link{run_mcmc}}. +} +\details{ +For further visualization (of the states), you can extract the posterior +samples with \code{as.data.frame} and \code{as_draws} methods to be used for example +with the \code{bayesplot} or \code{ggplot2} packages. +} +\note{ +For IS-MCMC, these plots correspond to the approximate (non-weighted) +samples +. +} +\examples{ +data("negbin_model") +# Note the very small number of iterations, so the plots look bad +plot(negbin_model) +} +\seealso{ +\code{\link{check_diagnostics}} for a quick diagnostics statistics +of the model. +} diff --git a/man/poisson_series.Rd b/man/poisson_series.Rd index 46f75a0a..99187048 100644 --- a/man/poisson_series.Rd +++ b/man/poisson_series.Rd @@ -3,12 +3,13 @@ \docType{data} \name{poisson_series} \alias{poisson_series} -\title{Simulated Poisson time series data} +\title{Simulated Poisson Time Series Data} \format{ -A vector of length 100 +A vector of length 100. } \description{ -See example for code for reproducing the data. +See example for code for reproducing the data. This was used in +Vihola, Helske, Franks (2020). } \examples{ # The data was generated as follows: @@ -16,4 +17,9 @@ set.seed(321) slope <- cumsum(c(0, rnorm(99, sd = 0.01))) y <- rpois(100, exp(cumsum(slope + c(0, rnorm(99, sd = 0.1))))) } +\references{ +Vihola, M, Helske, J, Franks, J (2020). Importance sampling type +estimators based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 +} \keyword{datasets} diff --git a/man/post_correct.Rd b/man/post_correct.Rd index fbf64414..a382b106 100644 --- a/man/post_correct.Rd +++ b/man/post_correct.Rd @@ -16,34 +16,42 @@ post_correct( \arguments{ \item{model}{Model of class \code{nongaussian} or \code{ssm_nlg}.} -\item{mcmc_output}{An output from \code{run_mcmc} used to compute the MAP estimate of theta. -While the intended use assumes this is from approximate MCMC, it is not actually checked, i.e., -it is also possible to input previous (asymptotically) exact output.} +\item{mcmc_output}{An output from \code{run_mcmc} used to compute the MAP +estimate of theta. +While the intended use assumes this is from approximate MCMC, it is not +actually checked, i.e., it is also possible to input previous +(asymptotically) exact output.} -\item{particles}{Number of particles for \eqn{\psi}-APF.} +\item{particles}{Number of particles for \eqn{\psi}-APF (positive integer). +Suitable values depend on the model and the data, but often relatively +small value less than say 50 is enough. See also \code{suggest_N}} -\item{threads}{Number of parallel threads.} +\item{threads}{Number of parallel threads (positive integer, default is 1).} -\item{is_type}{Type of IS-correction. Possible choices are -\code{"is3"} for simple importance sampling (weight is computed for each MCMC iteration independently), +\item{is_type}{Type of IS-correction. Possible choices are +\code{"is3"} for simple importance sampling (weight is computed for each +MCMC iteration independently), \code{"is2"} for jump chain importance sampling type weighting (default), or -\code{"is1"} for importance sampling type weighting where the number of particles used for -weight computations is proportional to the length of the jump chain block.} +\code{"is1"} for importance sampling type weighting where the number of +particles used forweight computations is proportional to the length of the +jump chain block.} -\item{seed}{Seed for the random number generator.} +\item{seed}{Seed for the C++ RNG (positive integer).} } \value{ -List with suggested number of particles \code{N} and matrix containing -estimated standard deviations of the log-weights and corresponding number of particles. +The original object of class \code{mcmc_output} with updated +weights, log-posterior values and state samples or summaries (depending on +the \code{mcmc_output$mcmc_type}). } \description{ -Function \code{post_correct} updates previously obtained approximate MCMC output -with post-correction weights leading to asymptotically exact weighted posterior, -and returns updated MCMC output where components \code{weights}, \code{posterior}, -\code{alpha}, \code{alphahat}, and \code{Vt} are updated (depending on the original output type). +Function \code{post_correct} updates previously obtained approximate MCMC +output with post-correction weights leading to asymptotically exact +weighted posterior, and returns updated MCMC output where components +\code{weights}, \code{posterior}, \code{alpha}, \code{alphahat}, and +\code{Vt} are updated (depending on the original output type). } \examples{ -\dontrun{ +\donttest{ set.seed(1) n <- 300 x1 <- sin((2 * pi / 12) * 1:n) @@ -62,7 +70,7 @@ y <- rbinom(n, size = u, plogis(0.5 * x1 + x2 + alpha)) ts.plot(y / u) model <- ar1_ng(y, distribution = "binomial", - rho = uniform(0.5, -1, 1), sigma = gamma(1, 2, 0.001), + rho = uniform(0.5, -1, 1), sigma = gamma_prior(1, 2, 0.001), mu = normal(0, 0, 10), xreg = cbind(x1,x2), beta = normal(c(0, 0), 0, 5), u = u) @@ -80,43 +88,46 @@ summary(out_is2, return_se = TRUE) # latent state library("dplyr") library("ggplot2") -state_approx <- as.data.frame(out_approx, variable = "states") \%>\% - group_by(time) \%>\% +state_approx <- as.data.frame(out_approx, variable = "states") |> + group_by(time) |> summarise(mean = mean(value)) -state_exact <- as.data.frame(out_is2, variable = "states") \%>\% - group_by(time) \%>\% +state_exact <- as.data.frame(out_is2, variable = "states") |> + group_by(time) |> summarise(mean = weighted.mean(value, weight)) dplyr::bind_rows(approx = state_approx, - exact = state_exact, .id = "method") \%>\% - filter(time > 200) \%>\% + exact = state_exact, .id = "method") |> + filter(time > 200) |> ggplot(aes(time, mean, colour = method)) + geom_line() + theme_bw() # posterior means p_approx <- predict(out_approx, model, type = "mean", - nsim = 1000, future = FALSE) \%>\% - group_by(time) \%>\% + nsim = 1000, future = FALSE) |> + group_by(time) |> summarise(mean = mean(value)) p_exact <- predict(out_is2, model, type = "mean", - nsim = 1000, future = FALSE) \%>\% - group_by(time) \%>\% + nsim = 1000, future = FALSE) |> + group_by(time) |> summarise(mean = mean(value)) -dplyr:: bind_rows(approx = p_approx, - exact = p_exact, .id = "method") \%>\% - filter(time > 200) \%>\% +dplyr::bind_rows(approx = p_approx, + exact = p_exact, .id = "method") |> + filter(time > 200) |> ggplot(aes(time, mean, colour = method)) + geom_line() + theme_bw() } } \references{ -A. Doucet, M. K. Pitt, G. Deligiannidis, R. Kohn, -Efficient implementation of Markov chain Monte Carlo when using an unbiased likelihood estimator, -Biometrika, Volume 102, Issue 2, 2015, Pages 295–313, https://doi.org/10.1093/biomet/asu075 -Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +Doucet A, Pitt M K, Deligiannidis G, Kohn R (2018). +Efficient implementation of Markov chain Monte Carlo when using an unbiased +likelihood estimator. Biometrika, 102, 2, 295-313, +https://doi.org/10.1093/biomet/asu075 + +Vihola M, Helske J, Franks J (2020). Importance sampling type estimators +based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 } diff --git a/man/predict.Rd b/man/predict.mcmc_output.Rd similarity index 50% rename from man/predict.Rd rename to man/predict.mcmc_output.Rd index 53ea1a85..606a6076 100644 --- a/man/predict.Rd +++ b/man/predict.mcmc_output.Rd @@ -2,51 +2,63 @@ % Please edit documentation in R/predict.R \name{predict.mcmc_output} \alias{predict.mcmc_output} +\alias{predict} \title{Predictions for State Space Models} \usage{ \method{predict}{mcmc_output}( object, model, - type = "response", nsim, + type = "response", future = TRUE, seed = sample(.Machine$integer.max, size = 1), ... ) } \arguments{ -\item{object}{mcmc_output object obtained from -\code{\link{run_mcmc}}} +\item{object}{Results object of class \code{mcmc_output} from +\code{\link{run_mcmc}}.} -\item{model}{Model for future observations. -Should have same structure as the original model which was used in MCMC, -in order to plug the posterior samples of the model parameters to the right places. -It is also possible to input the original model, which can be useful for example for -posterior predictive checks. In this case, set argument \code{future} to \code{FALSE}.} +\item{model}{A \code{bssm_model} object. +Should have same structure and class as the original model which was used in +\code{run_mcmc}, in order to plug the posterior samples of the model +parameters to the right places. +It is also possible to input the original model for obtaining predictions +for past time points. In this case, set argument +\code{future} to \code{FALSE}.} -\item{type}{Return predictions on \code{"mean"} -\code{"response"}, or \code{"state"} level.} +\item{nsim}{Positive integer defining number of samples to draw. Should be +less than or equal to \code{sum(object$counts)} i.e. the number of samples +in the MCMC output. Default is to use all the samples.} -\item{nsim}{Number of samples to draw.} +\item{type}{Type of predictions. Possible choices are +\code{"mean"} \code{"response"}, or \code{"state"} level.} -\item{future}{Default is \code{TRUE}, in which case predictions are future. +\item{future}{Default is \code{TRUE}, in which case predictions are for the +future, using posterior samples of (theta, alpha_T+1) i.e. the +posterior samples of hyperparameters and latest states. Otherwise it is assumed that \code{model} corresponds to the original model.} -\item{seed}{Seed for RNG.} +\item{seed}{Seed for the C++ RNG (positive integer). Note that this affects +only the C++ side, and \code{predict} also uses R side RNG for subsampling, +so for replicable results you should call \code{set.seed} before +\code{predict}.} \item{...}{Ignored.} } \value{ -Data frame of predicted samples. +A data.frame consisting of samples from the predictive +posterior distribution. } \description{ -Draw samples from the posterior predictive distribution for future time points -given the posterior draws of hyperparameters \eqn{\theta} and \eqn{alpha_{n+1}}. -Function can also be used to draw samples from the posterior predictive distribution -\eqn{p(\tilde y_1, \ldots, \tilde y_n | y_1,\ldots, y_n)}. +Draw samples from the posterior predictive distribution for future +time points given the posterior draws of hyperparameters \eqn{\theta} and +latent state \eqn{alpha_{n+1}} returned by \code{run_mcmc}. +Function can also be used to draw samples from the posterior predictive +distribution \eqn{p(\tilde y_1, \ldots, \tilde y_n | y_1,\ldots, y_n)}. } \examples{ -require("graphics") +library("graphics") y <- log10(JohnsonJohnson) prior <- uniform(0.01, 0, 1) model <- bsm_lg(window(y, end = c(1974, 4)), sd_y = prior, @@ -58,39 +70,39 @@ future_model$y <- ts(rep(NA, 25), start = tsp(model$y)[2] + 2 * deltat(model$y), frequency = frequency(model$y)) # use "state" for illustrative purposes, we could use type = "mean" directly -pred <- predict(mcmc_results, future_model, type = "state", +pred <- predict(mcmc_results, model = future_model, type = "state", nsim = 1000) -require("dplyr") -sumr_fit <- as.data.frame(mcmc_results, variable = "states") \%>\% - group_by(time, iter) \%>\% +library("dplyr") +sumr_fit <- as.data.frame(mcmc_results, variable = "states") |> + group_by(time, iter) |> mutate(signal = value[variable == "level"] + - value[variable == "seasonal_1"]) \%>\% - group_by(time) \%>\% + value[variable == "seasonal_1"]) |> + group_by(time) |> summarise(mean = mean(signal), lwr = quantile(signal, 0.025), upr = quantile(signal, 0.975)) -sumr_pred <- pred \%>\% - group_by(time, sample) \%>\% +sumr_pred <- pred |> + group_by(time, sample) |> mutate(signal = value[variable == "level"] + - value[variable == "seasonal_1"]) \%>\% - group_by(time) \%>\% + value[variable == "seasonal_1"]) |> + group_by(time) |> summarise(mean = mean(signal), lwr = quantile(signal, 0.025), upr = quantile(signal, 0.975)) # If we used type = "mean", we could do -# sumr_pred <- pred \%>\% -# group_by(time) \%>\% +# sumr_pred <- pred |> +# group_by(time) |> # summarise(mean = mean(value), # lwr = quantile(value, 0.025), # upr = quantile(value, 0.975)) -require("ggplot2") -rbind(sumr_fit, sumr_pred) \%>\% +library("ggplot2") +rbind(sumr_fit, sumr_pred) |> ggplot(aes(x = time, y = mean)) + geom_ribbon(aes(ymin = lwr, ymax = upr), fill = "#92f0a8", alpha = 0.25) + @@ -101,27 +113,28 @@ rbind(sumr_fit, sumr_pred) \%>\% time = time(JohnsonJohnson))) # Posterior predictions for past observations: -yrep <- predict(mcmc_results, model, type = "response", +yrep <- predict(mcmc_results, model = model, type = "response", future = FALSE, nsim = 1000) -meanrep <- predict(mcmc_results, model, type = "mean", +meanrep <- predict(mcmc_results, model = model, type = "mean", future = FALSE, nsim = 1000) -sumr_yrep <- yrep \%>\% - group_by(time) \%>\% +sumr_yrep <- yrep |> + group_by(time) |> summarise(earnings = mean(value), lwr = quantile(value, 0.025), - upr = quantile(value, 0.975)) \%>\% + upr = quantile(value, 0.975)) |> mutate(interval = "Observations") -sumr_meanrep <- meanrep \%>\% - group_by(time) \%>\% +sumr_meanrep <- meanrep |> + group_by(time) |> summarise(earnings = mean(value), lwr = quantile(value, 0.025), - upr = quantile(value, 0.975)) \%>\% + upr = quantile(value, 0.975)) |> mutate(interval = "Mean") -rbind(sumr_meanrep, sumr_yrep) \%>\% - mutate(interval = factor(interval, levels = c("Observations", "Mean"))) \%>\% +rbind(sumr_meanrep, sumr_yrep) |> + mutate(interval = + factor(interval, levels = c("Observations", "Mean"))) |> ggplot(aes(x = time, y = earnings)) + geom_ribbon(aes(ymin = lwr, ymax = upr, fill = interval), alpha = 0.75) + @@ -132,3 +145,6 @@ rbind(sumr_meanrep, sumr_yrep) \%>\% } +\seealso{ +\code{fitted} for in-sample predictions. +} diff --git a/man/print.mcmc_output.Rd b/man/print.mcmc_output.Rd index 5ddf0ba6..be8b2c62 100644 --- a/man/print.mcmc_output.Rd +++ b/man/print.mcmc_output.Rd @@ -7,10 +7,14 @@ \method{print}{mcmc_output}(x, ...) } \arguments{ -\item{x}{Output from \code{\link{run_mcmc}}.} +\item{x}{Object of class \code{mcmc_output} from \code{\link{run_mcmc}}.} \item{...}{Ignored.} } \description{ -Prints some basic summaries from the MCMC run by \code{\link{run_mcmc}}. +Prints some basic summaries from the MCMC run by \code{\link{run_mcmc}}. +} +\examples{ +data("negbin_model") +print(negbin_model) } diff --git a/man/priors.Rd b/man/priors.Rd deleted file mode 100644 index b4a42999..00000000 --- a/man/priors.Rd +++ /dev/null @@ -1,53 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/priors.R -\name{uniform} -\alias{uniform} -\alias{halfnormal} -\alias{normal} -\alias{tnormal} -\alias{gamma} -\title{Prior objects for bssm models} -\usage{ -uniform(init, min, max) - -halfnormal(init, sd) - -normal(init, mean, sd) - -tnormal(init, mean, sd, min = -Inf, max = Inf) - -gamma(init, shape, rate) -} -\arguments{ -\item{init}{Initial value for the parameter, used in initializing the model components and as a starting value -in MCMC.} - -\item{min}{Lower bound of the uniform and truncated normal prior.} - -\item{max}{Upper bound of the uniform and truncated normal prior.} - -\item{sd}{Standard deviation of the (underlying i.e. non-truncated) Normal distribution.} - -\item{mean}{Mean of the Normal prior.} - -\item{shape}{Shape parameter of the Gamma prior.} - -\item{rate}{Rate parameter of the Gamma prior.} -} -\value{ -object of class \code{bssm_prior}. -} -\description{ -These simple objects of class \code{bssm_prior} are used to construct a prior distributions for the -MCMC runs of \code{bssm} package. Currently supported priors are uniform (\code{uniform()}), -half-normal (\code{halfnormal()}), normal (\code{normal()}), gamma (\code{gamma}), and -truncated normal distribution (\code{tnormal()}).All parameters are vectorized so -for regression coefficient vector beta you can define prior for example -as \code{normal(0, 0, c(10, 20))}. -} -\examples{ -# create uniform prior on [-1, 1] for one parameter with initial value 0.2: -uniform(0.2, -1, 1) -# two normal priors at once i.e. for coefficients beta: -normal(init = c(0.1, 2), mean = 0, sd = c(1, 2)) -} diff --git a/man/run_mcmc.Rd b/man/run_mcmc.Rd index 27735894..960cf687 100644 --- a/man/run_mcmc.Rd +++ b/man/run_mcmc.Rd @@ -2,26 +2,382 @@ % Please edit documentation in R/run_mcmc.R \name{run_mcmc} \alias{run_mcmc} +\alias{run_mcmc.lineargaussian} +\alias{run_mcmc.nongaussian} +\alias{run_mcmc.ssm_nlg} +\alias{run_mcmc.ssm_sde} \title{Bayesian Inference of State Space Models} \usage{ -run_mcmc(model, iter, ...) +run_mcmc(model, ...) + +\method{run_mcmc}{lineargaussian}( + model, + iter, + output_type = "full", + burnin = floor(iter/2), + thin = 1, + gamma = 2/3, + target_acceptance = 0.234, + S, + end_adaptive_phase = FALSE, + threads = 1, + seed = sample(.Machine$integer.max, size = 1), + verbose, + ... +) + +\method{run_mcmc}{nongaussian}( + model, + iter, + particles, + output_type = "full", + mcmc_type = "is2", + sampling_method = "psi", + burnin = floor(iter/2), + thin = 1, + gamma = 2/3, + target_acceptance = 0.234, + S, + end_adaptive_phase = FALSE, + local_approx = TRUE, + threads = 1, + seed = sample(.Machine$integer.max, size = 1), + max_iter = 100, + conv_tol = 1e-08, + verbose, + ... +) + +\method{run_mcmc}{ssm_nlg}( + model, + iter, + particles, + output_type = "full", + mcmc_type = "is2", + sampling_method = "bsf", + burnin = floor(iter/2), + thin = 1, + gamma = 2/3, + target_acceptance = 0.234, + S, + end_adaptive_phase = FALSE, + threads = 1, + seed = sample(.Machine$integer.max, size = 1), + max_iter = 100, + conv_tol = 1e-08, + iekf_iter = 0, + verbose, + ... +) + +\method{run_mcmc}{ssm_sde}( + model, + iter, + particles, + output_type = "full", + mcmc_type = "is2", + L_c, + L_f, + burnin = floor(iter/2), + thin = 1, + gamma = 2/3, + target_acceptance = 0.234, + S, + end_adaptive_phase = FALSE, + threads = 1, + seed = sample(.Machine$integer.max, size = 1), + verbose, + ... +) } \arguments{ -\item{model}{State space model model of \code{bssm} package.} +\item{model}{Model of class \code{bssm_model}.} + +\item{...}{Ignored.} + +\item{iter}{A positive integer defining the total number of MCMC iterations. +Suitable value depends on the model, data, and the choice of specific +algorithms (\code{mcmc_type} and \code{sampling_method}). As increasing +\code{iter} also increases run time, it is is generally good idea to first +test the performance with a small values, e.g., less than 10000.} + +\item{output_type}{Either \code{"full"} +(default, returns posterior samples from the posterior +\eqn{p(\alpha, \theta | y)}), \code{"theta"} (for marginal posterior of +theta), or \code{"summary"} (return the mean and variance estimates of the +states and posterior samples of theta). See details.} + +\item{burnin}{A positive integer defining the length of the burn-in period +which is disregarded from the results. Defaults to \code{iter / 2}. +Note that all MCMC algorithms of \code{bssm} use adaptive MCMC during the +burn-in period in order to find good proposal distribution.} + +\item{thin}{A positive integer defining the thinning rate. All the MCMC +algorithms in \code{bssm} use the jump chain representation (see refs), +and the thinning is applied to these blocks. Defaults to 1. +For IS-corrected methods, larger value can also be +statistically more effective. Note: With \code{output_type = "summary"}, +the thinning does not affect the computations of the summary statistics in +case of pseudo-marginal methods.} + +\item{gamma}{Tuning parameter for the adaptation of RAM algorithm. Must be +between 0 and 1.} + +\item{target_acceptance}{Target acceptance rate for MCMC. Defaults to 0.234. +Must be between 0 and 1.} + +\item{S}{Matrix defining the initial value for the lower triangular matrix +of the RAM algorithm, so that the covariance matrix of the Gaussian proposal +distribution is \eqn{SS'}. Note that for some parameters +(currently the standard deviation, dispersion, and autoregressive parameters +of the BSM and AR(1) models) the sampling is done in unconstrained parameter +space, i.e. internal_theta = log(theta) (and logit(rho) or AR coefficient).} + +\item{end_adaptive_phase}{Logical, if \code{TRUE}, S is held fixed after the +burnin period. Default is \code{FALSE}.} + +\item{threads}{Number of threads for state simulation. Positive integer +(default is 1). +Note that parallel computing is only used in the post-correction phase of +IS-MCMC and when sampling the states in case of (approximate) Gaussian +models.} -\item{iter}{Number of MCMC iterations.} +\item{seed}{Seed for the C++ RNG (positive integer).} -\item{...}{Parameters to specific methods. See \code{\link{run_mcmc.gaussian}}, -\code{\link{run_mcmc.nongaussian}}, \code{\link{run_mcmc.ssm_nlg}}, -and \code{\link{run_mcmc.ssm_sde}} for details.} +\item{verbose}{If \code{TRUE}, prints a progress bar to the console. If +missing, defined by \code{rlang::is_interactive}. +Set to \code{FALSE} if number of iterations is less than 50.} + +\item{particles}{A positive integer defining the number of state samples per +MCMC iteration for models other than linear-Gaussian models. +Ignored if \code{mcmc_type} is \code{"approx"} or \code{"ekf"}. Suitable +values depend on the model, the data, \code{mcmc_type} and +\code{sampling_method}. While large values provide more +accurate estimates, the run time also increases with respect to to the +number of particles, so it is generally a good idea to test the run time +firstwith a small number of particles, e.g., less than 100.} + +\item{mcmc_type}{What type of MCMC algorithm should be used for models other +than linear-Gaussian models? Possible choices are +\code{"pm"} for pseudo-marginal MCMC, +\code{"da"} for delayed acceptance version of PMCMC , +\code{"approx"} for approximate inference based on the Gaussian +approximation of the model, +\code{"ekf"} for approximate inference using extended Kalman filter +(for \code{ssm_nlg}), +or one of the three importance sampling type weighting schemes: +\code{"is3"} for simple importance sampling (weight is computed for each +MCMC iteration independently), +\code{"is2"} for jump chain importance sampling type weighting (default), or +\code{"is1"} for importance sampling type weighting where the number of +particles used for +weight computations is proportional to the length of the jump chain block.} + +\item{sampling_method}{Method for state sampling when for models other than +linear-Gaussian models. If \code{"psi"}, \eqn{\psi}-APF is used (default). +If \code{"spdk"}, non-sequential importance sampling +based on Gaussian approximation is used. If \code{"bsf"}, bootstrap filter +is used. If \code{"ekf"}, particle filter based on EKF-proposals are used +(only for \code{ssm_nlg} models).} + +\item{local_approx}{If \code{TRUE} (default), Gaussian approximation +needed for some of the methods is performed at each iteration. +If \code{FALSE}, approximation is updated only once at the start of the +MCMC using the initial model.} + +\item{max_iter}{Maximum number of iterations used in Gaussian approximation, +as a positive integer. +Default is 100 (although typically only few iterations are needed).} + +\item{conv_tol}{Positive tolerance parameter used in Gaussian approximation.} + +\item{iekf_iter}{Non-negative integer. The default zero corresponds to +normal EKF, whereas \code{iekf_iter > 0} corresponds to iterated EKF +with \code{iekf_iter} iterations. Used only for models of class +\code{ssm_nlg}.} + +\item{L_c, L_f}{For \code{ssm_sde} models, Positive integer values defining +the discretization levels for first and second stages (defined as 2^L). +For pseudo-marginal methods (\code{"pm"}), maximum of these is used.} +} +\value{ +An object of class \code{mcmc_output}. } \description{ -Adaptive Markov chain Monte Carlo simulation of state space models using -Robust Adaptive Metropolis algorithm by Vihola (2012). -See specific methods for various model types for details. +Adaptive Markov chain Monte Carlo simulation for SSMs using +Robust Adaptive Metropolis algorithm by Vihola (2012). Several different +MCMC sampling schemes are implemented, see parameter +arguments, package vignette, Vihola, Helske, Franks (2020) and Helske and +Vihola (2021) for details. +} +\details{ +For linear-Gaussian models, option \code{"summary"} does not simulate +states directly but computes the posterior means and variances of states +using fast Kalman smoothing. This is slightly faster, +more memory efficient and more accurate than calculations based on +simulation smoother. In other cases, the means and +covariances are computed using the full output of particle filter +instead of subsampling one of these as in case of +\code{output_type = "full"}. The states are sampled up to the time point n+1 +where n is the length of the input time series i.e. the last values are +one-step-ahead predictions. (for predicting further, see +\code{?predict.mcmc_output}). + +Initial values for the sampling are taken from the model object +(\code{model$theta}). If you want to continue from previous run, you can +reconstruct your original model by plugging in the previously obtained +parameters to \code{model$theta}, providing the S matrix for the RAM +algorithm and setting \code{burnin = 0}. See example. Note however, that +this is not identical as running all the iterations once, due to the +RNG "discontinuity" and because even without burnin bssm does include +"theta_0" i.e. the initial theta in the final chain (even with +\code{burnin=0}). +} +\examples{ +model <- ar1_lg(LakeHuron, rho = uniform(0.5,-1,1), + sigma = halfnormal(1, 10), mu = normal(500, 500, 500), + sd_y = halfnormal(1, 10)) + +mcmc_results <- run_mcmc(model, iter = 2e4) +summary(mcmc_results, return_se = TRUE) + +sumr <- summary(mcmc_results, variable = "states") +library("ggplot2") +ggplot(sumr, aes(time, Mean)) + + geom_ribbon(aes(ymin = `2.5\%`, ymax = `97.5\%`), alpha = 0.25) + + geom_line() + theme_bw() + + geom_point(data = data.frame(Mean = LakeHuron, time = time(LakeHuron)), + col = 2) + +# Continue from the previous run +model$theta[] <- mcmc_results$theta[nrow(mcmc_results$theta), ] +run_more <- run_mcmc(model, S = mcmc_results$S, iter = 1000, burnin = 0) + +set.seed(1) +n <- 50 +slope <- cumsum(c(0, rnorm(n - 1, sd = 0.001))) +level <- cumsum(slope + c(0, rnorm(n - 1, sd = 0.2))) +y <- rpois(n, exp(level)) +poisson_model <- bsm_ng(y, + sd_level = halfnormal(0.01, 1), + sd_slope = halfnormal(0.01, 0.1), + P1 = diag(c(10, 0.1)), distribution = "poisson") + +# Note small number of iterations for CRAN checks +mcmc_out <- run_mcmc(poisson_model, iter = 1000, particles = 10, + mcmc_type = "da") +summary(mcmc_out, what = "theta", return_se = TRUE) + +set.seed(123) +n <- 50 +sd_level <- 0.1 +drift <- 0.01 +beta <- -0.9 +phi <- 5 + +level <- cumsum(c(5, drift + rnorm(n - 1, sd = sd_level))) +x <- 3 + (1:n) * drift + sin(1:n + runif(n, -1, 1)) +y <- rnbinom(n, size = phi, mu = exp(beta * x + level)) + +model <- bsm_ng(y, xreg = x, + beta = normal(0, 0, 10), + phi = halfnormal(1, 10), + sd_level = halfnormal(0.1, 1), + sd_slope = halfnormal(0.01, 0.1), + a1 = c(0, 0), P1 = diag(c(10, 0.1)^2), + distribution = "negative binomial") + +# run IS-MCMC +# Note small number of iterations for CRAN checks +fit <- run_mcmc(model, iter = 4000, + particles = 10, mcmc_type = "is2", seed = 1) + +# extract states +d_states <- as.data.frame(fit, variable = "states", time = 1:n) + +library("dplyr") +library("ggplot2") + + # compute summary statistics +level_sumr <- d_states |> + filter(variable == "level") |> + group_by(time) |> + summarise(mean = diagis::weighted_mean(value, weight), + lwr = diagis::weighted_quantile(value, weight, + 0.025), + upr = diagis::weighted_quantile(value, weight, + 0.975)) + +# visualize +level_sumr |> ggplot(aes(x = time, y = mean)) + + geom_line() + + geom_line(aes(y = lwr), linetype = "dashed", na.rm = TRUE) + + geom_line(aes(y = upr), linetype = "dashed", na.rm = TRUE) + + theme_bw() + + theme(legend.title = element_blank()) + + xlab("Time") + ylab("Level") + +# theta +d_theta <- as.data.frame(fit, variable = "theta") +ggplot(d_theta, aes(x = value)) + + geom_density(aes(weight = weight), adjust = 2, fill = "#92f0a8") + + facet_wrap(~ variable, scales = "free") + + theme_bw() + + +# Bivariate Poisson model: + +set.seed(1) +x <- cumsum(c(3, rnorm(19, sd = 0.5))) +y <- cbind( + rpois(20, exp(x)), + rpois(20, exp(x))) + +prior_fn <- function(theta) { + # half-normal prior using transformation + dnorm(exp(theta), 0, 1, log = TRUE) + theta # plus jacobian term +} + +update_fn <- function(theta) { + list(R = array(exp(theta), c(1, 1, 1))) +} + +model <- ssm_mng(y = y, Z = matrix(1,2,1), T = 1, + R = 0.1, P1 = 1, distribution = "poisson", + init_theta = log(0.1), + prior_fn = prior_fn, update_fn = update_fn) + +# Note small number of iterations for CRAN checks +out <- run_mcmc(model, iter = 4000, mcmc_type = "approx") + +sumr <- as.data.frame(out, variable = "states") |> + group_by(time) |> mutate(value = exp(value)) |> + summarise(mean = mean(value), + ymin = quantile(value, 0.05), ymax = quantile(value, 0.95)) +ggplot(sumr, aes(time, mean)) + +geom_ribbon(aes(ymin = ymin, ymax = ymax),alpha = 0.25) + +geom_line() + +geom_line(data = data.frame(mean = y[, 1], time = 1:20), + colour = "tomato") + +geom_line(data = data.frame(mean = y[, 2], time = 1:20), + colour = "tomato") + +theme_bw() + } \references{ -Matti Vihola (2012). "Robust adaptive Metropolis algorithm with -coerced acceptance rate". Statistics and Computing, Volume 22, Issue 5, -pages 997--1008. +Vihola M (2012). Robust adaptive Metropolis algorithm with +coerced acceptance rate. Statistics and Computing, 22(5), p 997-1008. +https://doi.org/10.1007/s11222-011-9269-5 + +Vihola, M, Helske, J, Franks, J (2020). Importance sampling type +estimators based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 + +Helske J, Vihola M (2021). bssm: Bayesian Inference of Non-linear and +Non-Gaussian State Space Models in R. The R Journal (2021) 13:2, 578-589. +https://doi.org/10.32614/RJ-2021-103 + +Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based +on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 2020; 1-38. https://doi.org/10.1111/sjos.12492 } diff --git a/man/run_mcmc.ssm_nlg.Rd b/man/run_mcmc.ssm_nlg.Rd deleted file mode 100644 index d009d3af..00000000 --- a/man/run_mcmc.ssm_nlg.Rd +++ /dev/null @@ -1,102 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/run_mcmc.R -\name{run_mcmc.ssm_nlg} -\alias{run_mcmc.ssm_nlg} -\title{Bayesian Inference of non-linear state space models} -\usage{ -\method{run_mcmc}{ssm_nlg}( - model, - iter, - particles, - output_type = "full", - mcmc_type = "is2", - sampling_method = "bsf", - burnin = floor(iter/2), - thin = 1, - gamma = 2/3, - target_acceptance = 0.234, - S, - end_adaptive_phase = FALSE, - threads = 1, - seed = sample(.Machine$integer.max, size = 1), - max_iter = 100, - conv_tol = 1e-08, - iekf_iter = 0, - ... -) -} -\arguments{ -\item{model}{Model model.} - -\item{iter}{Number of MCMC iterations.} - -\item{particles}{Number of state samples per MCMC iteration. -Ignored if \code{mcmc_type} is \code{"approx"} or \code{"ekf"}.} - -\item{output_type}{Either \code{"full"} -(default, returns posterior samples of states alpha and hyperparameters theta), -\code{"theta"} (for marginal posterior of theta), -or \code{"summary"} (return the mean and variance estimates of the states -and posterior samples of theta). In case of \code{"summary"}, means and -covariances are computed using the full output of particle filter -instead of sampling one of these as in case of \code{output_type = "full"}.} - -\item{mcmc_type}{What MCMC algorithm to use? Possible choices are -\code{"pm"} for pseudo-marginal MCMC, -\code{"da"} for delayed acceptance version of pseudo-marginal MCMC, -\code{"approx"} for approximate inference based on the Gaussian approximation of the model, -\code{"ekf"} for approximate inference using extended Kalman filter, -or one of the three importance sampling type weighting schemes: -\code{"is3"} for simple importance sampling (weight is computed for each MCMC iteration independently), -\code{"is2"} for jump chain importance sampling type weighting (default), or -\code{"is1"} for importance sampling type weighting where the number of particles used for -weight computations is proportional to the length of the jump chain block.} - -\item{sampling_method}{If \code{"bsf"} (default), bootstrap filter is used for state sampling. -If \code{"ekf"}, particle filter based on EKF-proposals are used. -If \code{"psi"}, \eqn{\psi}-APF is used.} - -\item{burnin}{Length of the burn-in period which is disregarded from the -results. Defaults to \code{iter / 2}.} - -\item{thin}{Thinning rate. Defaults to 1. Increase for large models in -order to save memory. For IS-corrected methods, larger -value can also be statistically more effective. -Note: With \code{output_type = "summary"}, the thinning does not affect the computations -of the summary statistics in case of pseudo-marginal methods.} - -\item{gamma}{Tuning parameter for the adaptation of RAM algorithm. Must be -between 0 and 1 (not checked).} - -\item{target_acceptance}{Target acceptance ratio for RAM. Defaults to 0.234. -For DA-MCMC, this corresponds to first stage acceptance rate, i.e., the total acceptance -rate will be smaller.} - -\item{S}{Initial value for the lower triangular matrix of RAM -algorithm, so that the covariance matrix of the Gaussian proposal -distribution is \eqn{SS'}. Note that for some parameters -(currently the standard deviation and dispersion parameters of bsm_ng models) the sampling -is done for transformed parameters with internal_theta = log(theta).} - -\item{end_adaptive_phase}{If \code{TRUE}, S is held fixed after the burnin period. Default is \code{FALSE}.} - -\item{threads}{Number of threads for state simulation.} - -\item{seed}{Seed for the random number generator.} - -\item{max_iter}{Maximum number of iterations used in Gaussian approximation.} - -\item{conv_tol}{Tolerance parameter used in Gaussian approximation.} - -\item{iekf_iter}{If \code{iekf_iter > 0}, iterated extended Kalman filter is used with -\code{iekf_iter} iterations in place of standard EKF. Defaults to zero.} - -\item{...}{Ignored.} -} -\description{ -Methods for posterior inference of states and parameters. -} -\references{ -Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 -} diff --git a/man/run_mcmc.ssm_sde.Rd b/man/run_mcmc.ssm_sde.Rd deleted file mode 100644 index 96ca4548..00000000 --- a/man/run_mcmc.ssm_sde.Rd +++ /dev/null @@ -1,90 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/run_mcmc.R -\name{run_mcmc.ssm_sde} -\alias{run_mcmc.ssm_sde} -\title{Bayesian Inference of SDE} -\usage{ -\method{run_mcmc}{ssm_sde}( - model, - iter, - particles, - output_type = "full", - mcmc_type = "is2", - L_c, - L_f, - burnin = floor(iter/2), - thin = 1, - gamma = 2/3, - target_acceptance = 0.234, - S, - end_adaptive_phase = FALSE, - threads = 1, - seed = sample(.Machine$integer.max, size = 1), - ... -) -} -\arguments{ -\item{model}{Model model.} - -\item{iter}{Number of MCMC iterations.} - -\item{particles}{Number of state samples per MCMC iteration.} - -\item{output_type}{Either \code{"full"} -(default, returns posterior samples of states alpha and hyperparameters theta), -\code{"theta"} (for marginal posterior of theta), -or \code{"summary"} (return the mean and variance estimates of the states -and posterior samples of theta). In case of \code{"summary"}, means and -covariances are computed using the full output of particle filter -instead of sampling one of these as in case of \code{output_type = "full"}. -If \code{particles = 0}, this is argument ignored and set to \code{"theta"}.} - -\item{mcmc_type}{What MCMC algorithm to use? Possible choices are -\code{"pm"} for pseudo-marginal MCMC, -\code{"da"} for delayed acceptance version of pseudo-marginal MCMC, -or one of the three importance sampling type weighting schemes: -\code{"is3"} for simple importance sampling (weight is computed for each MCMC iteration independently), -\code{"is2"} for jump chain importance sampling type weighting (default), or -\code{"is1"} for importance sampling type weighting where the number of particles used for -weight computations is proportional to the length of the jump chain block.} - -\item{L_c, L_f}{Integer values defining the discretization levels for first and second stages (defined as 2^L). -For PM methods, maximum of these is used.} - -\item{burnin}{Length of the burn-in period which is disregarded from the -results. Defaults to \code{iter / 2}.} - -\item{thin}{Thinning rate. Defaults to 1. Increase for large models in -order to save memory. For IS-corrected methods, larger -value can also be statistically more effective. -Note: With \code{output_type = "summary"}, the thinning does not affect the computations -of the summary statistics in case of pseudo-marginal methods.} - -\item{gamma}{Tuning parameter for the adaptation of RAM algorithm. Must be -between 0 and 1 (not checked).} - -\item{target_acceptance}{Target acceptance ratio for RAM. Defaults to 0.234. -For DA-MCMC, this corresponds to first stage acceptance rate, i.e., the total acceptance -rate will be smaller.} - -\item{S}{Initial value for the lower triangular matrix of RAM -algorithm, so that the covariance matrix of the Gaussian proposal -distribution is \eqn{SS'}. Note that for some parameters -(currently the standard deviation and dispersion parameters of bsm_ng models) the sampling -is done for transformed parameters with internal_theta = log(theta).} - -\item{end_adaptive_phase}{If \code{TRUE}, S is held fixed after the burnin period. Default is \code{FALSE}.} - -\item{threads}{Number of threads for state simulation.} - -\item{seed}{Seed for the random number generator.} - -\item{...}{Ignored.} -} -\description{ -Methods for posterior inference of states and parameters. -} -\references{ -Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 -} diff --git a/man/run_mcmc_g.Rd b/man/run_mcmc_g.Rd deleted file mode 100644 index c9ef2fbb..00000000 --- a/man/run_mcmc_g.Rd +++ /dev/null @@ -1,88 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/run_mcmc.R -\name{run_mcmc.gaussian} -\alias{run_mcmc.gaussian} -\title{Bayesian Inference of Linear-Gaussian State Space Models} -\usage{ -\method{run_mcmc}{gaussian}( - model, - iter, - output_type = "full", - burnin = floor(iter/2), - thin = 1, - gamma = 2/3, - target_acceptance = 0.234, - S, - end_adaptive_phase = FALSE, - threads = 1, - seed = sample(.Machine$integer.max, size = 1), - ... -) -} -\arguments{ -\item{model}{Model model.} - -\item{iter}{Number of MCMC iterations.} - -\item{output_type}{Type of output. Default is \code{"full"}, which returns -samples from the posterior \eqn{p(\alpha, \theta)}. Option \code{"summary"} does not simulate -states directly but computes the posterior means and variances of states using -fast Kalman smoothing. This is slightly faster, more memory efficient and -more accurate than calculations based on simulation smoother. Using option \code{"theta"} will only -return samples from the marginal posterior of the hyperparameters \eqn{\theta}.} - -\item{burnin}{Length of the burn-in period which is disregarded from the -results. Defaults to \code{iter / 2}. Note that all MCMC algorithms of \code{bssm} - used adaptive MCMC during the burn-in period in order to find good proposal.} - -\item{thin}{Thinning rate. All MCMC algorithms in \code{bssm} use the jump chain -representation, and the thinning is applied to these blocks. -Defaults to 1.} - -\item{gamma}{Tuning parameter for the adaptation of RAM algorithm. Must be -between 0 and 1 (not checked).} - -\item{target_acceptance}{Target acceptance rate for MCMC. Defaults to 0.234.} - -\item{S}{Initial value for the lower triangular matrix of RAM -algorithm, so that the covariance matrix of the Gaussian proposal -distribution is \eqn{SS'}. Note that for some parameters -(currently the standard deviation and dispersion parameters of bsm_lg models) the sampling -is done for transformed parameters with internal_theta = log(theta).} - -\item{end_adaptive_phase}{If \code{TRUE}, S is held fixed after the burnin period. Default is \code{FALSE}.} - -\item{threads}{Number of threads for state simulation. The default is 1.} - -\item{seed}{Seed for the random number generator.} - -\item{...}{Ignored.} -} -\description{ -Bayesian Inference of Linear-Gaussian State Space Models -} -\examples{ -model <- ar1_lg(LakeHuron, rho = uniform(0.5,-1,1), - sigma = halfnormal(1, 10), mu = normal(500, 500, 500), - sd_y = halfnormal(1, 10)) - -mcmc_results <- run_mcmc(model, iter = 2e4) -summary(mcmc_results, return_se = TRUE) - -require("dplyr") -sumr <- as.data.frame(mcmc_results, variable = "states") \%>\% - group_by(time) \%>\% - summarise(mean = mean(value), - lwr = quantile(value, 0.025), - upr = quantile(value, 0.975)) -require("ggplot2") -sumr \%>\% ggplot(aes(time, mean)) + - geom_ribbon(aes(ymin = lwr, ymax = upr),alpha=0.25) + - geom_line() + theme_bw() + - geom_point(data = data.frame(mean = LakeHuron, time = time(LakeHuron)), - col = 2) -} -\references{ -Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 -} diff --git a/man/run_mcmc_ng.Rd b/man/run_mcmc_ng.Rd deleted file mode 100644 index 0a87fd14..00000000 --- a/man/run_mcmc_ng.Rd +++ /dev/null @@ -1,211 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/run_mcmc.R -\name{run_mcmc.nongaussian} -\alias{run_mcmc.nongaussian} -\title{Bayesian Inference of Non-Gaussian State Space Models} -\usage{ -\method{run_mcmc}{nongaussian}( - model, - iter, - particles, - output_type = "full", - mcmc_type = "is2", - sampling_method = "psi", - burnin = floor(iter/2), - thin = 1, - gamma = 2/3, - target_acceptance = 0.234, - S, - end_adaptive_phase = FALSE, - local_approx = TRUE, - threads = 1, - seed = sample(.Machine$integer.max, size = 1), - max_iter = 100, - conv_tol = 1e-08, - ... -) -} -\arguments{ -\item{model}{Model model.} - -\item{iter}{Number of MCMC iterations.} - -\item{particles}{Number of state samples per MCMC iteration. -Ignored if \code{mcmc_type} is \code{"approx"}.} - -\item{output_type}{Either \code{"full"} -(default, returns posterior samples of states alpha and hyperparameters theta), -\code{"theta"} (for marginal posterior of theta), -or \code{"summary"} (return the mean and variance estimates of the states -and posterior samples of theta). In case of \code{"summary"}, means and -covariances are computed using the full output of particle filter -instead of sampling one of these as in case of \code{output_type = "full"}.} - -\item{mcmc_type}{What MCMC algorithm to use? Possible choices are -\code{"pm"} for pseudo-marginal MCMC, -\code{"da"} for delayed acceptance version of PMCMC , -\code{"approx"} for approximate inference based on the Gaussian approximation of the model, -or one of the three importance sampling type weighting schemes: -\code{"is3"} for simple importance sampling (weight is computed for each MCMC iteration independently), -\code{"is2"} for jump chain importance sampling type weighting (default), or -\code{"is1"} for importance sampling type weighting where the number of particles used for -weight computations is proportional to the length of the jump chain block.} - -\item{sampling_method}{If \code{"psi"}, \eqn{\psi}-APF is used for state sampling -(default). If \code{"spdk"}, non-sequential importance sampling based -on Gaussian approximation is used. If \code{"bsf"}, bootstrap filter -is used.} - -\item{burnin}{Length of the burn-in period which is disregarded from the -results. Defaults to \code{iter / 2}.} - -\item{thin}{Thinning rate. Defaults to 1. Increase for large models in -order to save memory. For IS-corrected methods, larger -value can also be statistically more effective. -Note: With \code{output_type = "summary"}, the thinning does not affect the computations -of the summary statistics in case of pseudo-marginal methods.} - -\item{gamma}{Tuning parameter for the adaptation of RAM algorithm. Must be -between 0 and 1 (not checked).} - -\item{target_acceptance}{Target acceptance rate for MCMC. Defaults to 0.234. -For DA-MCMC, this corresponds to first stage acceptance rate, i.e., the total acceptance -rate will be smaller.} - -\item{S}{Initial value for the lower triangular matrix of RAM -algorithm, so that the covariance matrix of the Gaussian proposal -distribution is \eqn{SS'}. Note that for some parameters -(currently the standard deviation and dispersion parameters of bsm_ng models) the sampling -is done for transformed parameters with internal_theta = log(theta).} - -\item{end_adaptive_phase}{If \code{TRUE}, S is held fixed after the burnin period. Default is \code{FALSE}.} - -\item{local_approx}{If \code{TRUE} (default), Gaussian approximation needed for -importance sampling is performed at each iteration. If \code{FALSE}, approximation is updated only -once at the start of the MCMC using the initial model.} - -\item{threads}{Number of threads for state simulation. The default is 1. -Note that parallel computing is only used in the post-correction phase of IS-MCMC -and when sampling the states in case of approximate models.} - -\item{seed}{Seed for the random number generator.} - -\item{max_iter}{Maximum number of iterations used in Gaussian approximation.} - -\item{conv_tol}{Tolerance parameter used in Gaussian approximation.} - -\item{...}{Ignored.} -} -\description{ -Methods for posterior inference of states and parameters. -} -\examples{ -set.seed(1) -n <- 50 -slope <- cumsum(c(0, rnorm(n - 1, sd = 0.001))) -level <- cumsum(slope + c(0, rnorm(n - 1, sd = 0.2))) -y <- rpois(n, exp(level)) -poisson_model <- bsm_ng(y, - sd_level = halfnormal(0.01, 1), - sd_slope = halfnormal(0.01, 0.1), - P1 = diag(c(10, 0.1)), distribution = "poisson") - -# Note small number of iterations for CRAN checks -mcmc_out <- run_mcmc(poisson_model, iter = 1000, particles = 10, - mcmc_type = "da") -summary(mcmc_out, what = "theta", return_se = TRUE) - -set.seed(123) -n <- 50 -sd_level <- 0.1 -drift <- 0.01 -beta <- -0.9 -phi <- 5 - -level <- cumsum(c(5, drift + rnorm(n - 1, sd = sd_level))) -x <- 3 + (1:n) * drift + sin(1:n + runif(n, -1, 1)) -y <- rnbinom(n, size = phi, mu = exp(beta * x + level)) - -model <- bsm_ng(y, xreg = x, - beta = normal(0, 0, 10), - phi = halfnormal(1, 10), - sd_level = halfnormal(0.1, 1), - sd_slope = halfnormal(0.01, 0.1), - a1 = c(0, 0), P1 = diag(c(10, 0.1)^2), - distribution = "negative binomial") - -# run IS-MCMC -# Note small number of iterations for CRAN checks -fit <- run_mcmc(model, iter = 5000, - particles = 10, mcmc_type = "is2", seed = 1) - -# extract states -d_states <- as.data.frame(fit, variable = "states", time = 1:n) - -library("dplyr") -library("ggplot2") - - # compute summary statistics -level_sumr <- d_states \%>\% - filter(variable == "level") \%>\% - group_by(time) \%>\% - summarise(mean = Hmisc::wtd.mean(value, weight, normwt = TRUE), - lwr = Hmisc::wtd.quantile(value, weight, - 0.025, normwt = TRUE), - upr = Hmisc::wtd.quantile(value, weight, - 0.975, normwt = TRUE)) - -# visualize -level_sumr \%>\% ggplot(aes(x = time, y = mean)) + - geom_line() + - geom_line(aes(y = lwr), linetype = "dashed", na.rm = TRUE) + - geom_line(aes(y = upr), linetype = "dashed", na.rm = TRUE) + - theme_bw() + - theme(legend.title = element_blank()) + - xlab("Time") + ylab("Level") - -# theta -d_theta <- as.data.frame(fit, variable = "theta") -ggplot(d_theta, aes(x = value)) + - geom_density(aes(weight = weight), adjust = 2, fill = "#92f0a8") + - facet_wrap(~ variable, scales = "free") + - theme_bw() - - -# Bivariate Poisson model: - -set.seed(1) -x <- cumsum(c(3, rnorm(19, sd = 0.5))) -y <- cbind( - rpois(20, exp(x)), - rpois(20, exp(x))) - -prior_fn <- function(theta) { - # half-normal prior using transformation - dnorm(exp(theta), 0, 1, log = TRUE) + theta # plus jacobian term -} - -update_fn <- function(theta) { - list(R = array(exp(theta), c(1, 1, 1))) -} - -model <- ssm_mng(y = y, Z = matrix(1,2,1), T = 1, - R = 0.1, P1 = 1, distribution = "poisson", - init_theta = log(0.1), - prior_fn = prior_fn, update_fn = update_fn) - -# Note small number of iterations for CRAN checks -out <- run_mcmc(model, iter = 5000, mcmc_type = "approx") - -sumr <- as.data.frame(out, variable = "states") \%>\% - group_by(time) \%>\% mutate(value = exp(value)) \%>\% - summarise(mean = mean(value), - ymin = quantile(value, 0.05), ymax = quantile(value, 0.95)) -ggplot(sumr, aes(time, mean)) + -geom_ribbon(aes(ymin = ymin, ymax = ymax),alpha = 0.25) + -geom_line() + -geom_line(data = data.frame(mean = y[, 1], time = 1:20), colour = "tomato") + -geom_line(data = data.frame(mean = y[, 2], time = 1:20), colour = "tomato") + -theme_bw() - -} diff --git a/man/sim_smoother.Rd b/man/sim_smoother.Rd index 6a15543e..24f59500 100644 --- a/man/sim_smoother.Rd +++ b/man/sim_smoother.Rd @@ -2,17 +2,17 @@ % Please edit documentation in R/sim_smoother.R \name{sim_smoother} \alias{sim_smoother} -\alias{sim_smoother.gaussian} +\alias{sim_smoother.lineargaussian} \alias{sim_smoother.nongaussian} \title{Simulation Smoothing} \usage{ -sim_smoother(model, nsim, seed, use_antithetic = FALSE, ...) +sim_smoother(model, nsim, seed, use_antithetic = TRUE, ...) -\method{sim_smoother}{gaussian}( +\method{sim_smoother}{lineargaussian}( model, nsim = 1, seed = sample(.Machine$integer.max, size = 1), - use_antithetic = FALSE, + use_antithetic = TRUE, ... ) @@ -20,19 +20,27 @@ sim_smoother(model, nsim, seed, use_antithetic = FALSE, ...) model, nsim = 1, seed = sample(.Machine$integer.max, size = 1), - use_antithetic = FALSE, + use_antithetic = TRUE, ... ) } \arguments{ -\item{model}{Model object.} +\item{model}{Model of class \code{bsm_lg}, \code{ar1_lg} +\code{ssm_ulg}, or \code{ssm_mlg}, or one of the non-gaussian models +\code{bsm_ng}, \code{ar1_ng} \code{svm}, +\code{ssm_ung}, or \code{ssm_mng}.} -\item{nsim}{Number of independent samples.} +\item{nsim}{Number of samples (positive integer). Suitable values +depend on the model and the data, and while larger values provide more +accurate estimates, the run time also increases with respect to to the +number of samples, so it is generally a good idea to test the filter first +with a small number of samples, e.g., less than 100.} -\item{seed}{Seed for the random number generator.} +\item{seed}{Seed for the C++ RNG (positive integer).} -\item{use_antithetic}{Use an antithetic variable for location. -Default is \code{FALSE}. Ignored for multivariate models.} +\item{use_antithetic}{Logical. If \code{TRUE} (default), use antithetic +variable for location in simulation smoothing. Ignored for \code{ssm_mng} +models.} \item{...}{Ignored.} } @@ -40,15 +48,20 @@ Default is \code{FALSE}. Ignored for multivariate models.} An array containing the generated samples. } \description{ -Function \code{sim_smoother} performs simulation smoothing i.e. simulates the states -from the conditional distribution \eqn{p(\alpha | y, \theta)} for linear-Gaussian models. +Function \code{sim_smoother} performs simulation smoothing i.e. simulates +the states from the conditional distribution \eqn{p(\alpha | y, \theta)} +for linear-Gaussian models. } \details{ -For non-Gaussian/non-linear models, the simulation is based on the approximating -Gaussian model. +For non-Gaussian/non-linear models, the simulation is based on the +approximating Gaussian model. } \examples{ -model <- bsm_lg(rep(NA, 50), sd_level = uniform(1,0,5), sd_y = uniform(1,0,5)) -sim <- sim_smoother(model, 12) +# only missing data, simulates from prior +model <- bsm_lg(rep(NA, 25), sd_level = 1, + sd_y = 1) +# use antithetic variable for location +sim <- sim_smoother(model, nsim = 4, use_antithetic = TRUE, seed = 1) ts.plot(sim[, 1, ]) +cor(sim[, 1, ]) } diff --git a/man/smoother.Rd b/man/smoother.Rd index 92185cd2..c21cab58 100644 --- a/man/smoother.Rd +++ b/man/smoother.Rd @@ -2,15 +2,24 @@ % Please edit documentation in R/smoother.R \name{fast_smoother} \alias{fast_smoother} +\alias{fast_smoother.lineargaussian} \alias{smoother} +\alias{smoother.lineargaussian} \title{Kalman Smoothing} \usage{ fast_smoother(model, ...) +\method{fast_smoother}{lineargaussian}(model, ...) + smoother(model, ...) + +\method{smoother}{lineargaussian}(model, ...) } \arguments{ -\item{model}{Model model.} +\item{model}{Model to be approximated. Should be of class +\code{bsm_ng}, \code{ar1_ng} \code{svm}, +\code{ssm_ung}, or \code{ssm_mng}, or \code{ssm_nlg}, i.e. non-gaussian or +non-linear \code{bssm_model}.} \item{...}{Ignored.} } @@ -24,5 +33,21 @@ computes only smoothed estimates of the states, and function \code{smoother} computes also smoothed variances. } \details{ -For non-Gaussian models, the smoothing is based on the approximate Gaussian model. +For non-Gaussian models, the smoothing is based on the approximate Gaussian +model. +} +\examples{ +model <- bsm_lg(Nile, + sd_level = tnormal(120, 100, 20, min = 0), + sd_y = tnormal(50, 50, 25, min = 0), + a1 = 1000, P1 = 200) +ts.plot(cbind(Nile, fast_smoother(model)), col = 1:2) +model <- bsm_lg(Nile, + sd_y = tnormal(120, 100, 20, min = 0), + sd_level = tnormal(50, 50, 25, min = 0), + a1 = 1000, P1 = 500^2) + +out <- smoother(model) +ts.plot(cbind(Nile, out$alphahat), col = 1:2) +ts.plot(sqrt(out$Vt[1, 1, ])) } diff --git a/man/ssm_mlg.Rd b/man/ssm_mlg.Rd index 29599474..a87bb895 100644 --- a/man/ssm_mlg.Rd +++ b/man/ssm_mlg.Rd @@ -10,84 +10,98 @@ ssm_mlg( H, T, R, - a1, - P1, + a1 = NULL, + P1 = NULL, init_theta = numeric(0), - D, - C, + D = NULL, + C = NULL, state_names, update_fn = default_update_fn, prior_fn = default_prior_fn ) } \arguments{ -\item{y}{Observations as multivariate time series or matrix with dimensions n x p.} +\item{y}{Observations as multivariate time series or matrix with +dimensions n x p.} -\item{Z}{System matrix Z of the observation equation as p x m matrix or p x m x n array.} +\item{Z}{System matrix Z of the observation equation as p x m matrix or +p x m x n array.} -\item{H}{Lower triangular matrix H of the observation. Either a scalar or a vector of length n.} +\item{H}{Lower triangular matrix H of the observation. Either a scalar or +a vector of length n.} \item{T}{System matrix T of the state equation. Either a m x m matrix or a m x m x n array.} -\item{R}{Lower triangular matrix R the state equation. Either a m x k matrix or a -m x k x n array.} +\item{R}{Lower triangular matrix R the state equation. Either a m x k matrix +or a m x k x n array.} \item{a1}{Prior mean for the initial state as a vector of length m.} \item{P1}{Prior covariance matrix for the initial state as m x m matrix.} -\item{init_theta}{Initial values for the unknown hyperparameters theta.} +\item{init_theta}{Initial values for the unknown hyperparameters theta +(i.e. unknown variables excluding latent state variables).} \item{D}{Intercept terms for observation equation, given as a p x n matrix.} \item{C}{Intercept terms for state equation, given as m x n matrix.} -\item{state_names}{Names for the states.} +\item{state_names}{A character vector defining the names of the states.} -\item{update_fn}{Function which returns list of updated model -components given input vector theta. This function should take only one +\item{update_fn}{A function which returns list of updated model +components given input vector theta. This function should take only one vector argument which is used to create list with elements named as -\code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and \code{C}, -where each element matches the dimensions of the original model. -If any of these components is missing, it is assumed to be constant wrt. theta.} - -\item{prior_fn}{Function which returns log of prior density +\code{Z}, \code{H}, \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and +\code{C}, where each element matches the dimensions of the original model +It's best to check the internal dimensions with \code{str(model_object)} as +the dimensions of input arguments can differ from the final dimensions. +If any of these components is missing, it is assumed to be constant wrt. +theta.} + +\item{prior_fn}{A function which returns log of prior density given input vector theta.} } \value{ -Object of class \code{ssm_mlg}. +An object of class \code{ssm_mlg}. } \description{ -Construct an object of class \code{ssm_mlg} by directly defining the corresponding terms of -the model. +Construct an object of class \code{ssm_mlg} by directly defining the +corresponding terms of the model. } \details{ -The general multivariate linear-Gaussian model is defined using the following -observational and state equations: +The general multivariate linear-Gaussian model is defined using the +following observational and state equations: -\deqn{y_t = D_t + Z_t \alpha_t + H_t \epsilon_t, (\textrm{observation equation})} -\deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, (\textrm{transition equation})} +\deqn{y_t = D_t + Z_t \alpha_t + H_t \epsilon_t, +(\textrm{observation equation})} +\deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, +(\textrm{transition equation})} where \eqn{\epsilon_t \sim N(0, I_p)}, \eqn{\eta_t \sim N(0, I_k)} and -\eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other. -Here p is the number of time series and k is the number of disturbance terms +\eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other. +Here p is the number of time series and k is the number of disturbance terms (which can be less than m, the number of states). -The \code{update_fn} function should take only one +The \code{update_fn} function should take only one vector argument which is used to create list with elements named as -\code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and \code{C}, +\code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, +and \code{C}, where each element matches the dimensions of the original model. -If any of these components is missing, it is assumed to be constant wrt. theta. -Note that while you can input say R as m x k matrix for \code{ssm_mlg}, -\code{update_fn} should return R as m x k x 1 in this case. +If any of these components is missing, it is assumed to be +constant wrt. theta. +Note that while you can input say R as m x k matrix for \code{ssm_mlg}, +\code{update_fn} should return R as m x k x 1 in this case. It might be useful to first construct the model without updating function } \examples{ data("GlobalTemp", package = "KFAS") model_temp <- ssm_mlg(GlobalTemp, H = matrix(c(0.15,0.05,0, 0.05), 2, 2), - R = 0.05, Z = matrix(1, 2, 1), T = 1, P1 = 10) -ts.plot(cbind(model_temp$y, smoother(model_temp)$alphahat),col=1:3) + R = 0.05, Z = matrix(1, 2, 1), T = 1, P1 = 10, + state_names = "temperature", + # using default values, but being explicit for testing purposes + D = matrix(0, 2, 1), C = matrix(0, 1, 1)) +ts.plot(cbind(model_temp$y, smoother(model_temp)$alphahat), col = 1:3) } diff --git a/man/ssm_mng.Rd b/man/ssm_mng.Rd index ebd1fbeb..ba1c7423 100644 --- a/man/ssm_mng.Rd +++ b/man/ssm_mng.Rd @@ -9,83 +9,131 @@ ssm_mng( Z, T, R, - a1, - P1, + a1 = NULL, + P1 = NULL, distribution, phi = 1, - u = 1, + u, init_theta = numeric(0), - D, - C, + D = NULL, + C = NULL, state_names, update_fn = default_update_fn, prior_fn = default_prior_fn ) } \arguments{ -\item{y}{Observations as multivariate time series or matrix with dimensions n x p.} +\item{y}{Observations as multivariate time series or matrix with dimensions +n x p.} -\item{Z}{System matrix Z of the observation equation as p x m matrix or p x m x n array.} +\item{Z}{System matrix Z of the observation equation as p x m matrix or +p x m x n array.} \item{T}{System matrix T of the state equation. Either a m x m matrix or a m x m x n array.} -\item{R}{Lower triangular matrix R the state equation. Either a m x k matrix or a +\item{R}{Lower triangular matrix R the state equation. Either a m x k +matrix or a m x k x n array.} \item{a1}{Prior mean for the initial state as a vector of length m.} \item{P1}{Prior covariance matrix for the initial state as m x m matrix.} -\item{distribution}{vector of distributions of the observed series. Possible choices are -\code{"poisson"}, \code{"binomial"}, \code{"negative binomial"}, \code{"gamma"}, -and \code{"gaussian"}.} +\item{distribution}{A vector of distributions of the observed series. +Possible choices are +\code{"poisson"}, \code{"binomial"}, \code{"negative binomial"}, +\code{"gamma"}, and \code{"gaussian"}.} \item{phi}{Additional parameters relating to the non-Gaussian distributions. -For negative binomial distribution this is the dispersion term, for gamma distribution -this is the shape parameter, for Gaussian this is standard deviation, -and for other distributions this is ignored.} +For negative binomial distribution this is the dispersion term, for +gamma distribution this is the shape parameter, for Gaussian this is +standard deviation, and for other distributions this is ignored.} -\item{u}{Constant parameter for non-Gaussian models. For Poisson, gamma, -and negative binomial distribution, this corresponds to the offset term. -For binomial, this is the number of trials.} +\item{u}{A matrix of positive constants for non-Gaussian models +(of same dimensions as y). For Poisson, gamma, and negative binomial +distribution, this corresponds to the offset term. For binomial, this is the +number of trials (and as such should be integer(ish)).} -\item{init_theta}{Initial values for the unknown hyperparameters theta.} +\item{init_theta}{Initial values for the unknown hyperparameters theta +(i.e. unknown variables excluding latent state variables).} \item{D}{Intercept terms for observation equation, given as p x n matrix.} \item{C}{Intercept terms for state equation, given as m x n matrix.} -\item{state_names}{Names for the states.} +\item{state_names}{A character vector defining the names of the states.} -\item{update_fn}{Function which returns list of updated model -components given input vector theta. This function should take only one +\item{update_fn}{A function which returns list of updated model +components given input vector theta. This function should take only one vector argument which is used to create list with elements named as \code{Z}, \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, \code{C}, and -\code{phi}, -where each element matches the dimensions of the original model. -If any of these components is missing, it is assumed to be constant wrt. theta.} +\code{phi}, where each element matches the dimensions of the original model. +If any of these components is missing, it is assumed to be constant wrt. +theta. It's best to check the internal dimensions with +\code{str(model_object)} as the dimensions of input arguments can differ +from the final dimensions.} -\item{prior_fn}{Function which returns log of prior density +\item{prior_fn}{A function which returns log of prior density given input vector theta.} } \value{ -Object of class \code{ssm_mng}. +An object of class \code{ssm_mng}. } \description{ -Construct an object of class \code{ssm_mng} by directly defining the corresponding terms of -the model. +Construct an object of class \code{ssm_mng} by directly defining the +corresponding terms of the model. } \details{ -The general multivariate non-Gaussian model is defined using the following +The general multivariate non-Gaussian model is defined using the following observational and state equations: \deqn{p^i(y^i_t | D_t + Z_t \alpha_t), (\textrm{observation equation})} -\deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, (\textrm{transition equation})} +\deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, +(\textrm{transition equation})} where \eqn{\eta_t \sim N(0, I_k)} and -\eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other, and \eqn{p^i(y_t | .)} -is either Poisson, binomial, gamma, Gaussian, or negative binomial distribution for -each observation series \eqn{i=1,...,p}.Here k is the number of disturbance terms -(which can be less than m, the number of states). +\eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other, and +\eqn{p^i(y_t | .)} is either Poisson, binomial, gamma, Gaussian, or +negative binomial distribution for each observation series \eqn{i=1,...,p}. +Here k is the number of disturbance terms (which can be less than m, +the number of states). +} +\examples{ + +set.seed(1) +n <- 20 +x <- cumsum(rnorm(n, sd = 0.5)) +phi <- 2 +y <- cbind( + rgamma(n, shape = phi, scale = exp(x) / phi), + rbinom(n, 10, plogis(x))) + +Z <- matrix(1, 2, 1) +T <- 1 +R <- 0.5 +a1 <- 0 +P1 <- 1 + +update_fn <- function(theta) { + list(R = array(theta[1], c(1, 1, 1)), phi = c(theta[2], 1)) +} + +prior_fn <- function(theta) { + ifelse(all(theta > 0), sum(dnorm(theta, 0, 1, log = TRUE)), -Inf) +} + +model <- ssm_mng(y, Z, T, R, a1, P1, phi = c(2, 1), + init_theta = c(0.5, 2), + distribution = c("gamma", "binomial"), + u = cbind(1, rep(10, n)), + update_fn = update_fn, prior_fn = prior_fn, + state_names = "random_walk", + # using default values, but being explicit for testing purposes + D = matrix(0, 2, 1), C = matrix(0, 1, 1)) + +# smoothing based on approximating gaussian model +ts.plot(cbind(y, fast_smoother(model)), + col = 1:3, lty = c(1, 1, 2)) + } diff --git a/man/ssm_nlg.Rd b/man/ssm_nlg.Rd index d0681cfc..cbf8b6fe 100644 --- a/man/ssm_nlg.Rd +++ b/man/ssm_nlg.Rd @@ -25,55 +25,91 @@ ssm_nlg( ) } \arguments{ -\item{y}{Observations as multivariate time series (or matrix) of length \eqn{n}.} +\item{y}{Observations as multivariate time series (or matrix) of length +\eqn{n}.} -\item{Z, H, T, R}{An external pointers for the C++ functions which -define the corresponding model functions.} +\item{Z, H, T, R}{An external pointers (object of class \code{externalptr}) +for the C++ functions which define the corresponding model functions.} -\item{Z_gn, T_gn}{An external pointers for the C++ functions which -define the gradients of the corresponding model functions.} +\item{Z_gn, T_gn}{An external pointers (object of class \code{externalptr}) +for the C++ functions which define the gradients of the corresponding model +functions.} -\item{a1}{Prior mean for the initial state as a vector of length m.} +\item{a1}{Prior mean for the initial state as object of class +\code{externalptr}} -\item{P1}{Prior covariance matrix for the initial state as m x m matrix.} +\item{P1}{Prior covariance matrix for the initial state as object of class +\code{externalptr}} \item{theta}{Parameter vector passed to all model functions.} -\item{known_params}{Vector of known parameters passed to all model functions.} +\item{known_params}{A vector of known parameters passed to all model +functions.} -\item{known_tv_params}{Matrix of known parameters passed to all model functions.} +\item{known_tv_params}{A matrix of known parameters passed to all model +functions.} -\item{n_states}{Number of states in the model.} +\item{n_states}{Number of states in the model (positive integer).} -\item{n_etas}{Dimension of the noise term of the transition equation.} +\item{n_etas}{Dimension of the noise term of the transition equation +(positive integer).} -\item{log_prior_pdf}{An external pointer for the C++ function which +\item{log_prior_pdf}{An external pointer (object of class +\code{externalptr}) for the C++ function which computes the log-prior density given theta.} -\item{time_varying}{Optional logical vector of length 4, denoting whether the values of +\item{time_varying}{Optional logical vector of length 4, denoting whether +the values of Z, H, T, and R vary with respect to time variable (given identical states). If used, this can speed up some computations.} -\item{state_names}{Names for the states.} +\item{state_names}{A character vector containing names for the states.} } \value{ -Object of class \code{ssm_nlg}. +An object of class \code{ssm_nlg}. } \description{ -Constructs an object of class \code{ssm_nlg} by defining the corresponding terms -of the observation and state equation. +Constructs an object of class \code{ssm_nlg} by defining the corresponding +terms of the observation and state equation. } \details{ The nonlinear Gaussian model is defined as -\deqn{y_t = Z(t, \alpha_t, \theta) + H(t, \theta) \epsilon_t, (\textrm{observation equation})} -\deqn{\alpha_{t+1} = T(t, \alpha_t, \theta) + R(t, \theta)\eta_t, (\textrm{transition equation})} +\deqn{y_t = Z(t, \alpha_t, \theta) + H(t, \theta) \epsilon_t, +(\textrm{observation equation})} +\deqn{\alpha_{t+1} = T(t, \alpha_t, \theta) + R(t, \theta)\eta_t, +(\textrm{transition equation})} where \eqn{\epsilon_t \sim N(0, I_p)}, \eqn{\eta_t \sim N(0, I_m)} and \eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other, and functions -\eqn{Z, H, T, R} can depend on \eqn{\alpha_t} and parameter vector \eqn{\theta}. +\eqn{Z, H, T, R} can depend on \eqn{\alpha_t} and parameter vector +\eqn{\theta}. Compared to other models, these general models need a bit more effort from the user, as you must provide the several small C++ snippets which define the -model structure. See examples in the vignette. +model structure. See examples in the vignette and \code{cpp_example_model}. +} +\examples{ +\donttest{ # Takes a while on CRAN +set.seed(1) +n <- 50 +x <- y <- numeric(n) +y[1] <- rnorm(1, exp(x[1]), 0.1) +for(i in 1:(n-1)) { + x[i+1] <- rnorm(1, sin(x[i]), 0.1) + y[i+1] <- rnorm(1, exp(x[i+1]), 0.1) +} + +pntrs <- cpp_example_model("nlg_sin_exp") + +model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(log_H = log(0.1), log_R = log(0.1)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state") + +out <- ekf(model_nlg, iekf_iter = 100) +ts.plot(cbind(x, out$at[1:n], out$att[1:n]), col = 1:3) +} } diff --git a/man/ssm_sde.Rd b/man/ssm_sde.Rd index 48dfc558..2e3642f8 100644 --- a/man/ssm_sde.Rd +++ b/man/ssm_sde.Rd @@ -17,13 +17,16 @@ ssm_sde( ) } \arguments{ -\item{y}{Observations as univariate time series (or vector) of length \eqn{n}.} +\item{y}{Observations as univariate time series (or vector) of length +\eqn{n}.} -\item{drift, diffusion, ddiffusion}{An external pointers for the C++ functions which +\item{drift, diffusion, ddiffusion}{An external pointers for the C++ functions +which define the drift, diffusion and derivative of diffusion functions of SDE.} \item{obs_pdf}{An external pointer for the C++ function which -computes the observational log-density given the the states and parameter vector theta.} +computes the observational log-density given the the states and parameter +vector theta.} \item{prior_pdf}{An external pointer for the C++ function which computes the prior log-density given the parameter vector theta.} @@ -36,7 +39,7 @@ computes the prior log-density given the parameter vector theta.} forced by \code{abs} in Milstein scheme.} } \value{ -Object of class \code{ssm_sde}. +An object of class \code{ssm_sde}. } \description{ Constructs an object of class \code{ssm_sde} by defining the functions for @@ -45,12 +48,14 @@ as well as the log-density of observation equation. We assume that the observations are measured at integer times (missing values are allowed). } \details{ -As in case of \code{ssm_nlg} models, these general models need a bit more effort from -the user, as you must provide the several small C++ snippets which define the -model structure. See vignettes for an example. +As in case of \code{ssm_nlg} models, these general models need a bit more +effort from the user, as you must provide the several small C++ snippets +which define the model structure. See vignettes for an example and +\code{cpp_example_model}. } \examples{ -\dontrun{ + +\donttest{ # Takes a while on CRAN library("sde") set.seed(1) # theta_0 = rho = 0.5 @@ -62,9 +67,8 @@ x <- sde.sim(t0 = 0, T = 50, X0 = 1, N = 50, sigma.x = expression(0)) y <- rpois(50, exp(x[-1])) -# Template can be found in the vignette -Rcpp::sourceCpp("ssm_sde_template.cpp") -pntrs <- create_xptrs() +# source c++ snippets +pntrs <- cpp_example_model("sde_poisson_OU") sde_model <- ssm_sde(y, pntrs$drift, pntrs$diffusion, pntrs$ddiffusion, pntrs$obs_density, pntrs$prior, @@ -77,10 +81,11 @@ ts.plot(cbind(x, est$alphahat, est$alphahat + 2*sqrt(c(est$Vt))), col = c(2, 1, 1, 1), lty = c(1, 1, 2, 2)) + # Takes time with finer mesh, parallelization with IS-MCMC helps a lot out <- run_mcmc(sde_model, L_c = 4, L_f = 8, particles = 50, iter = 2e4, threads = 4L) - } + } diff --git a/man/ssm_ulg.Rd b/man/ssm_ulg.Rd index e39706b4..932a0c80 100644 --- a/man/ssm_ulg.Rd +++ b/man/ssm_ulg.Rd @@ -10,11 +10,11 @@ ssm_ulg( H, T, R, - a1, - P1, + a1 = NULL, + P1 = NULL, init_theta = numeric(0), - D, - C, + D = NULL, + C = NULL, state_names, update_fn = default_update_fn, prior_fn = default_prior_fn @@ -23,61 +23,80 @@ ssm_ulg( \arguments{ \item{y}{Observations as time series (or vector) of length \eqn{n}.} -\item{Z}{System matrix Z of the observation equation as m x 1 or m x n matrix.} +\item{Z}{System matrix Z of the observation equation. Either a +vector of length m, +a m x n matrix, or object which can be coerced to such.} -\item{H}{Vector of standard deviations. Either a scalar or a vector of length n.} +\item{H}{A vector of standard deviations. Either a scalar or a vector of +length n.} \item{T}{System matrix T of the state equation. Either a m x m matrix or a -m x m x n array.} +m x m x n array, or object which can be coerced to such.} -\item{R}{Lower triangular matrix R the state equation. Either a m x k matrix or a -m x k x n array.} +\item{R}{Lower triangular matrix R the state equation. Either +a m x k matrix or a m x k x n array, or object which can be coerced to such.} \item{a1}{Prior mean for the initial state as a vector of length m.} \item{P1}{Prior covariance matrix for the initial state as m x m matrix.} -\item{init_theta}{Initial values for the unknown hyperparameters theta.} +\item{init_theta}{Initial values for the unknown hyperparameters theta +(i.e. unknown variables excluding latent state variables).} -\item{D}{Intercept terms for observation equation, given as a length n vector.} +\item{D}{Intercept terms \eqn{D_t} for the observations equation, given as a +scalar or vector of length n.} -\item{C}{Intercept terms for state equation, given as m x n matrix.} +\item{C}{Intercept terms \eqn{C_t} for the state equation, given as a +m times 1 or m times n matrix.} -\item{state_names}{Names for the states.} +\item{state_names}{A character vector defining the names of the states.} -\item{update_fn}{Function which returns list of updated model -components given input vector theta. See details.} - -\item{prior_fn}{Function which returns log of prior density +\item{update_fn}{A function which returns list of updated model +components given input vector theta. This function should take only one +vector argument which is used to create list with elements named as +\code{Z}, \code{H}, \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and +\code{C}, where each element matches the dimensions of the original model +It's best to check the internal dimensions with \code{str(model_object)} as +the dimensions of input arguments can differ from the final dimensions. +If any of these components is missing, it is assumed to be constant wrt. +theta.} + +\item{prior_fn}{A function which returns log of prior density given input vector theta.} } \value{ -Object of class \code{ssm_ulg}. +An object of class \code{ssm_ulg}. } \description{ -Construct an object of class \code{ssm_ulg} by directly defining the corresponding terms of -the model. +Construct an object of class \code{ssm_ulg} by directly defining the +corresponding terms of the model. } \details{ -The general univariate linear-Gaussian model is defined using the following +The general univariate linear-Gaussian model is defined using the following observational and state equations: -\deqn{y_t = D_t + Z_t \alpha_t + H_t \epsilon_t, (\textrm{observation equation})} -\deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, (\textrm{transition equation})} +\deqn{y_t = D_t + Z_t \alpha_t + H_t \epsilon_t, +(\textrm{observation equation})} +\deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, +(\textrm{transition equation})} where \eqn{\epsilon_t \sim N(0, 1)}, \eqn{\eta_t \sim N(0, I_k)} and \eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other. -Here k is the number of disturbance terms which can be less than m, the number of states. +Here k is the number of disturbance terms which can be less than m, the +number of states. -The \code{update_fn} function should take only one +The \code{update_fn} function should take only one vector argument which is used to create list with elements named as -\code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and \code{C}, +\code{Z}, \code{H} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, +and \code{C}, where each element matches the dimensions of the original model. -If any of these components is missing, it is assumed to be constant wrt. theta. -Note that while you can input say R as m x k matrix for \code{ssm_ulg}, -\code{update_fn} should return R as m x k x 1 in this case. -It might be useful to first construct the model without updating function and then check -the expected structure of the model components from the output. +If any of these components is missing, it is assumed to be constant wrt. +theta. +Note that while you can input say R as m x k matrix for \code{ssm_ulg}, +\code{update_fn} should return R as m x k x 1 in this case. +It might be useful to first construct the model without updating function +and then check the expected structure of the model components from the +output. } \examples{ @@ -105,24 +124,32 @@ update_fn <- function(theta) { } # prior for standard deviations as half-normal(1) prior_fn <- function(theta) { - if(any(theta < 0)){ - log_p <- -Inf + if(any(theta < 0)) { + log_p <- -Inf } else { - log_p <- sum(dnorm(theta, 0, 1, log = TRUE)) + log_p <- sum(dnorm(theta, 0, 1, log = TRUE)) } log_p } model <- ssm_ulg(y, Z, H, T, R, a1, P1, init_theta = c(1, 0.1, 0.1), - update_fn = update_fn, prior_fn = prior_fn) + update_fn = update_fn, prior_fn = prior_fn, + state_names = c("level", "b1", "b2"), + # using default values, but being explicit for testing purposes + C = matrix(0, 3, 1), D = numeric(1)) -out <- run_mcmc(model, iter = 10000) +out <- run_mcmc(model, iter = 5000) out -sumr <- summary(out, variable = "state") -ts.plot(sumr$Mean, col = 1:3) -lines(b1, col= 2, lty = 2) -lines(b2, col= 3, lty = 2) +sumr <- summary(out, variable = "state", times = 1:n) +sumr$true <- c(b1, b2, rep(1, n)) +library(ggplot2) +ggplot(sumr, aes(x = time, y = Mean)) + +geom_ribbon(aes(ymin = `2.5\%`, ymax = `97.5\%`), alpha = 0.5) + +geom_line() + +geom_line(aes(y = true), colour = "red") + +facet_wrap(~ variable, scales = "free") + +theme_bw() # Perhaps easiest way to construct a general SSM for bssm is to use the # model building functionality of KFAS: @@ -142,32 +169,36 @@ model_bssm <- as_bssm(model_kfas, kappa = 100) # "manually" by constructing only necessary matrices, # i.e., in this case a list with H and Q) -updatefn <- function(theta){ +prior_fn <- function(theta) { + if(any(theta < 0)) -Inf else sum(dnorm(theta, 0, 0.1, log = TRUE)) +} + +update_fn <- function(theta) { model_kfas <- SSModel(log(drivers) ~ SSMtrend(1, Q = theta[1]^2)+ SSMseasonal(period = 12, sea.type = "trigonometric", Q = theta[2]^2) + log(PetrolPrice) + law, data = Seatbelts, H = theta[3]^2) - as_bssm(model_kfas, kappa = 100) + # the bssm_model object is essentially list so this is fine + as_bssm(model_kfas, kappa = 100, init_theta = init_theta, + update_fn = update_fn, prior_fn = prior_fn) } -prior <- function(theta) { - if(any(theta < 0)) -Inf else sum(dnorm(theta, 0, 0.1, log = TRUE)) -} init_theta <- rep(1e-2, 3) -c("sd_level", "sd_seasonal", "sd_y") -model_bssm <- as_bssm(model_kfas, kappa = 100, - init_theta = init_theta, - prior_fn = prior, update_fn = updatefn) +names(init_theta) <- c("sd_level", "sd_seasonal", "sd_y") + +model_bssm <- update_fn(init_theta) -\dontrun{ +\donttest{ out <- run_mcmc(model_bssm, iter = 10000, burnin = 5000) out - -# Above the regression coefficients are modelled as time-invariant latent states. +} +# Above the regression coefficients are modelled as +# time-invariant latent states. # Here is an alternative way where we use variable D so that the -# coefficients are part of parameter vector theta: +# coefficients are part of parameter vector theta. Note however that the +# first option often preferable in order to keep the dimension of theta low. updatefn2 <- function(theta) { # note no PetrolPrice or law variables here @@ -193,7 +224,7 @@ model_bssm2 <- updatefn2(init_theta) model_bssm2$theta <- init_theta model_bssm2$prior_fn <- prior2 model_bssm2$update_fn <- updatefn2 - +\donttest{ out2 <- run_mcmc(model_bssm2, iter = 10000, burnin = 5000) out2 } diff --git a/man/ssm_ung.Rd b/man/ssm_ung.Rd index 96611a8c..57a390ba 100644 --- a/man/ssm_ung.Rd +++ b/man/ssm_ung.Rd @@ -9,14 +9,14 @@ ssm_ung( Z, T, R, - a1, - P1, + a1 = NULL, + P1 = NULL, distribution, phi = 1, - u = 1, + u, init_theta = numeric(0), - D, - C, + D = NULL, + C = NULL, state_names, update_fn = default_update_fn, prior_fn = default_prior_fn @@ -25,31 +25,36 @@ ssm_ung( \arguments{ \item{y}{Observations as time series (or vector) of length \eqn{n}.} -\item{Z}{System matrix Z of the observation equation. Either a vector of length m, +\item{Z}{System matrix Z of the observation equation. Either a +vector of length m, a m x n matrix, or object which can be coerced to such.} \item{T}{System matrix T of the state equation. Either a m x m matrix or a m x m x n array, or object which can be coerced to such.} -\item{R}{Lower triangular matrix R the state equation. Either a m x k matrix or a -m x k x n array, or object which can be coerced to such.} +\item{R}{Lower triangular matrix R the state equation. Either +a m x k matrix or a m x k x n array, or object which can be coerced to such.} \item{a1}{Prior mean for the initial state as a vector of length m.} \item{P1}{Prior covariance matrix for the initial state as m x m matrix.} -\item{distribution}{Distribution of the observed time series. Possible choices are -\code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and \code{"negative binomial"}.} +\item{distribution}{Distribution of the observed time series. Possible +choices are \code{"poisson"}, \code{"binomial"}, \code{"gamma"}, and +\code{"negative binomial"}.} \item{phi}{Additional parameter relating to the non-Gaussian distribution. -For negative binomial distribution this is the dispersion term, for gamma distribution -this is the shape parameter, and for other distributions this is ignored.} +For negative binomial distribution this is the dispersion term, for gamma +distribution this is the shape parameter, and for other distributions this +is ignored. Should an object of class \code{bssm_prior} or +a positive scalar.} -\item{u}{Constant parameter vector for non-Gaussian models. For Poisson, gamma, and -negative binomial distribution, this corresponds to the offset term. For binomial, -this is the number of trials.} +\item{u}{A vector of positive constants for non-Gaussian models. For +Poisson, gamma, and negative binomial distribution, this corresponds to the +offset term. For binomial, this is the number of trials.} -\item{init_theta}{Initial values for the unknown hyperparameters theta.} +\item{init_theta}{Initial values for the unknown hyperparameters theta +(i.e. unknown variables excluding latent state variables).} \item{D}{Intercept terms \eqn{D_t} for the observations equation, given as a scalar or vector of length n.} @@ -57,42 +62,55 @@ scalar or vector of length n.} \item{C}{Intercept terms \eqn{C_t} for the state equation, given as a m times 1 or m times n matrix.} -\item{state_names}{Names for the states.} +\item{state_names}{A character vector defining the names of the states.} -\item{update_fn}{Function which returns list of updated model -components given input vector theta. See details.} - -\item{prior_fn}{Function which returns log of prior density +\item{update_fn}{A function which returns list of updated model +components given input vector theta. This function should take only one +vector argument which is used to create list with elements named as +\code{Z}, \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, \code{C}, and +\code{phi}, where each element matches the dimensions of the original model. +If any of these components is missing, it is assumed to be constant wrt. +theta. It's best to check the internal dimensions with +\code{str(model_object)} as the dimensions of input arguments can differ +from the final dimensions.} + +\item{prior_fn}{A function which returns log of prior density given input vector theta.} } \value{ -Object of class \code{ssm_ung}. +An object of class \code{ssm_ung}. } \description{ -Construct an object of class \code{ssm_ung} by directly defining the corresponding terms of -the model. +Construct an object of class \code{ssm_ung} by directly defining the +corresponding terms of the model. } \details{ -The general univariate non-Gaussian model is defined using the following +The general univariate non-Gaussian model is defined using the following observational and state equations: \deqn{p(y_t | D_t + Z_t \alpha_t), (\textrm{observation equation})} -\deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, (\textrm{transition equation})} +\deqn{\alpha_{t+1} = C_t + T_t \alpha_t + R_t \eta_t, +(\textrm{transition equation})} where \eqn{\eta_t \sim N(0, I_k)} and \eqn{\alpha_1 \sim N(a_1, P_1)} independently of each other, -and \eqn{p(y_t | .)} is either Poisson, binomial, gamma, or negative binomial distribution. -Here k is the number of disturbance terms which can be less than m, the number of states. +and \eqn{p(y_t | .)} is either Poisson, binomial, gamma, or +negative binomial distribution. +Here k is the number of disturbance terms which can be less than m, +the number of states. -The \code{update_fn} function should take only one +The \code{update_fn} function should take only one vector argument which is used to create list with elements named as -\code{Z}, \code{phi} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, and \code{C}, +\code{Z}, \code{phi} \code{T}, \code{R}, \code{a1}, \code{P1}, \code{D}, +and \code{C}, where each element matches the dimensions of the original model. -If any of these components is missing, it is assumed to be constant wrt. theta. -Note that while you can input say R as m x k matrix for \code{ssm_ung}, -\code{update_fn} should return R as m x k x 1 in this case. -It might be useful to first construct the model without updating function and then check -the expected structure of the model components from the output. +If any of these components is missing, it is assumed to be constant +wrt. theta. +Note that while you can input say R as m x k matrix for \code{ssm_ung}, +\code{update_fn} should return R as m x k x 1 in this case. +It might be useful to first construct the model without updating function +and then check the expected structure of the model components from +the output. } \examples{ diff --git a/man/suggest_N.Rd b/man/suggest_N.Rd index f9a84f8f..691f2228 100644 --- a/man/suggest_N.Rd +++ b/man/suggest_N.Rd @@ -6,7 +6,7 @@ \usage{ suggest_N( model, - mcmc_output, + theta, candidates = seq(10, 100, by = 10), replications = 100, seed = sample(.Machine$integer.max, size = 1) @@ -15,36 +15,39 @@ suggest_N( \arguments{ \item{model}{Model of class \code{nongaussian} or \code{ssm_nlg}.} -\item{mcmc_output}{An output from \code{run_mcmc} used to compute the MAP estimate of theta. -While the intended use assumes this is from approximate MCMC, it is not actually checked, i.e., -it is also possible to input previous (asymptotically) exact output.} +\item{theta}{A vector of theta corresponding to the model, at which point +the standard deviation of the log-likelihood is computed. Typically MAP +estimate from the (approximate) MCMC run. Can also be an output from +\code{run_mcmc} which is then used to compute the MAP +estimate of theta.} -\item{candidates}{Vector containing the candidate number of particles to test. Default -is \code{seq(10, 100, by = 10)}.} +\item{candidates}{A vector of positive integers containing the candidate +number of particles to test. Default is \code{seq(10, 100, by = 10)}.} -\item{replications}{How many replications should be used for computing the standard deviations? -Default is 100.} +\item{replications}{Positive integer, how many replications should be used +for computing the standard deviations? Default is 100.} -\item{seed}{Seed for the random number generator.} +\item{seed}{Seed for the C++ RNG (positive integer).} } \value{ -List with suggested number of particles \code{N} and matrix containing -estimated standard deviations of the log-weights and corresponding number of particles. +List with suggested number of particles \code{N} and matrix +containing estimated standard deviations of the log-weights and +corresponding number of particles. } \description{ -Function \code{estimate_N} estimates suitable number particles needed for accurate -post-correction of approximate MCMC +Function \code{estimate_N} estimates suitable number particles needed for +accurate post-correction of approximate MCMC. } \details{ -Function \code{suggest_N} estimates the standard deviation of the -logarithm of the post-correction weights at approximate MAP of theta, -using various particle sizes and suggest smallest number of particles -which still leads standard deviation less than 1. Similar approach was suggested in -the context of pseudo-marginal MCMC by Doucet et al. (2015), but see also -Section 10.3 in Vihola et al (2020). +Function \code{suggest_N} estimates the standard deviation of the +logarithm of the post-correction weights at approximate MAP of theta, +using various particle sizes and suggest smallest number of particles +which still leads standard deviation less than 1. Similar approach was +suggested in the context of pseudo-marginal MCMC by Doucet et al. (2015), +but see also Section 10.3 in Vihola et al (2020). } \examples{ -\dontrun{ + set.seed(1) n <- 300 x1 <- sin((2 * pi / 12) * 1:n) @@ -52,7 +55,7 @@ x2 <- cos((2 * pi / 12) * 1:n) alpha <- numeric(n) alpha[1] <- 0 rho <- 0.7 -sigma <- 2 +sigma <- 1.2 mu <- 1 for(i in 2:n) { alpha[i] <- rnorm(1, mu * (1 - rho) + rho * alpha[i-1], sigma) @@ -63,23 +66,31 @@ y <- rbinom(n, size = u, plogis(0.5 * x1 + x2 + alpha)) ts.plot(y / u) model <- ar1_ng(y, distribution = "binomial", - rho = uniform(0.5, -1, 1), sigma = gamma(1, 2, 0.001), + rho = uniform(0.5, -1, 1), sigma = gamma_prior(1, 2, 0.001), mu = normal(0, 0, 10), xreg = cbind(x1,x2), beta = normal(c(0, 0), 0, 5), u = u) -out_approx <- run_mcmc(model, mcmc_type = "approx", - iter = 5000) +# theta from earlier approximate MCMC run +# out_approx <- run_mcmc(model, mcmc_type = "approx", +# iter = 5000) +# theta <- out_approx$theta[which.max(out_approx$posterior), ] + +theta <- c(rho = 0.64, sigma = 1.16, mu = 1.1, x1 = 0.56, x2 = 1.28) -estN <- suggest_N(model, out_approx, candidates = seq(10, 50, by = 10)) +estN <- suggest_N(model, theta, candidates = seq(10, 50, by = 10), + replications = 50, seed = 1) plot(x = estN$results$N, y = estN$results$sd, type = "b") estN$N -} + } \references{ -A. Doucet, M. K. Pitt, G. Deligiannidis, R. Kohn, -Efficient implementation of Markov chain Monte Carlo when using an unbiased likelihood estimator, -Biometrika, Volume 102, Issue 2, 2015, Pages 295–313, https://doi.org/10.1093/biomet/asu075 -Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +Doucet, A, Pitt, MK, Deligiannidis, G, Kohn, R (2015). +Efficient implementation of Markov chain Monte Carlo when using an +unbiased likelihood estimator, Biometrika, 102(2) p. 295-313, +https://doi.org/10.1093/biomet/asu075 + +Vihola, M, Helske, J, Franks, J (2020). Importance sampling type estimators +based on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 1-38. https://doi.org/10.1111/sjos.12492 } diff --git a/man/summary.mcmc_output.Rd b/man/summary.mcmc_output.Rd index 0562569f..31a3046c 100644 --- a/man/summary.mcmc_output.Rd +++ b/man/summary.mcmc_output.Rd @@ -1,35 +1,73 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/print_mcmc.R +% Please edit documentation in R/summary.R \name{summary.mcmc_output} \alias{summary.mcmc_output} -\title{Summary of MCMC object} +\title{Summary Statistics of Posterior Samples} \usage{ -\method{summary}{mcmc_output}(object, return_se = FALSE, variable = "theta", only_theta = FALSE, ...) +\method{summary}{mcmc_output}( + object, + return_se = FALSE, + variable = "theta", + probs = c(0.025, 0.975), + times, + states, + use_times = TRUE, + method = "sokal", + ... +) } \arguments{ \item{object}{Output from \code{run_mcmc}} -\item{return_se}{if \code{FALSE} (default), computation of standard -errors and effective sample sizes is omitted.} +\item{return_se}{if \code{FALSE} (default), computation of standard +errors and effective sample sizes is omitted (as they can take considerable +time for models with large number of states and time points).} -\item{variable}{Are the summary statistics computed for either \code{"theta"} (default), -\code{"states"}, or \code{"both"}?} +\item{variable}{Are the summary statistics computed for either +\code{"theta"} (default), \code{"states"}, or \code{"both"}?} -\item{only_theta}{Deprecated. If \code{TRUE}, summaries are computed only for hyperparameters theta.} +\item{probs}{A numeric vector defining the quantiles of interest. Default is +\code{c(0.025, 0.975)}.} + +\item{times}{A vector of indices. For states, for what time points the +summaries should be computed? Default is all, ignored if +\code{variable = "theta"}.} + +\item{states}{A vector of indices. For what states the summaries should be +computed?. Default is all, ignored if +\code{variable = "theta"}.} + +\item{use_times}{If \code{TRUE} (default), transforms the values of the time +variable to match the ts attribute of the input to define. If \code{FALSE}, +time is based on the indexing starting from 1.} + +\item{method}{Method for computing integrated autocorrelation time. Default +is \code{"sokal"}, other option is \code{"geyer"}.} \item{...}{Ignored.} } +\value{ +If \code{variable} is \code{"theta"} or \code{"states"}, a +\code{data.frame} object. If \code{"both"}, a list of two data frames. +} \description{ -This functions returns a list containing mean, standard deviations, standard errors, and -effective sample size estimates for parameters and states. +This functions returns a data frame containing mean, standard deviations, +standard errors, and effective sample size estimates for parameters and +states. } \details{ -For IS-MCMC two types of standard errors are reported. +For IS-MCMC two types of standard errors are reported. SE-IS can be regarded as the square root of independent IS variance, -whereas SE corresponds to the square root of total asymptotic variance ( -see Remark 3 of Vihola et al. (2020)). +whereas SE corresponds to the square root of total asymptotic variance +(see Remark 3 of Vihola et al. (2020)). +} +\examples{ +data("negbin_model") +summary(negbin_model, return_se = TRUE, method = "geyer") +summary(negbin_model, times = c(1, 200), prob = c(0.05, 0.5, 0.95)) } \references{ -Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based on approximate marginal Markov chain Monte Carlo. -Scand J Statist. 2020; 1– 38. https://doi.org/10.1111/sjos.12492 +Vihola, M, Helske, J, Franks, J. Importance sampling type estimators based +on approximate marginal Markov chain Monte Carlo. +Scand J Statist. 2020; 1-38. https://doi.org/10.1111/sjos.12492 } diff --git a/man/svm.Rd b/man/svm.Rd index e589ff67..7f047940 100644 --- a/man/svm.Rd +++ b/man/svm.Rd @@ -7,43 +7,75 @@ svm(y, mu, rho, sd_ar, sigma) } \arguments{ -\item{y}{Vector or a \code{\link{ts}} object of observations.} +\item{y}{A numeric vector or a \code{\link{ts}} object of observations.} -\item{mu}{Prior for mu parameter of transition equation.} +\item{mu}{A prior for mu parameter of transition equation. +Should be an object of class \code{bssm_prior}.} -\item{rho}{prior for autoregressive coefficient.} +\item{rho}{A prior for autoregressive coefficient. +Should be an object of class \code{bssm_prior}.} -\item{sd_ar}{Prior for the standard deviation of noise of the AR-process.} +\item{sd_ar}{A prior for the standard deviation of noise of the AR-process. +Should be an object of class \code{bssm_prior}.} -\item{sigma}{Prior for sigma parameter of observation equation, internally denoted as phi. Ignored -if \code{mu} is provided. Note that typically parametrization using mu is preferred due to -better numerical properties and availability of better Gaussian approximation. -Most notably the global approximation approach does not work with sigma parameterization as -sigma is not a parameter of the resulting approximate model.} +\item{sigma}{A prior for sigma parameter of observation equation, internally +denoted as phi. Should be an object of class \code{bssm_prior}. +Ignored if \code{mu} is provided. Note that typically +parametrization using mu is preferred due to better numerical properties and +availability of better Gaussian approximation. +Most notably the global approximation approach does not work with sigma +parameterization as sigma is not a parameter of the resulting approximate +model.} } \value{ -Object of class \code{svm}. +An object of class \code{svm}. } \description{ Constructs a simple stochastic volatility model with Gaussian errors and -first order autoregressive signal. +first order autoregressive signal. See the main vignette for details. } \examples{ data("exchange") -exchange <- exchange[1:100] # faster CRAN check -model <- svm(exchange, rho = uniform(0.98,-0.999,0.999), +y <- exchange[1:100] # for faster CRAN check +model <- svm(y, rho = uniform(0.98, -0.999, 0.999), sd_ar = halfnormal(0.15, 5), sigma = halfnormal(0.6, 2)) obj <- function(pars) { - -logLik(svm(exchange, rho = uniform(pars[1],-0.999,0.999), - sd_ar = halfnormal(pars[2],sd=5), - sigma = halfnormal(pars[3],sd=2)), particles = 0) + -logLik(svm(y, + rho = uniform(pars[1], -0.999, 0.999), + sd_ar = halfnormal(pars[2], 5), + sigma = halfnormal(pars[3], 2)), particles = 0) } -opt <- nlminb(c(0.98, 0.15, 0.6), obj, lower = c(-0.999, 1e-4, 1e-4), upper = c(0.999,10,10)) +opt <- optim(c(0.98, 0.15, 0.6), obj, + lower = c(-0.999, 1e-4, 1e-4), + upper = c(0.999, 10, 10), method = "L-BFGS-B") pars <- opt$par -model <- svm(exchange, rho = uniform(pars[1],-0.999,0.999), - sd_ar = halfnormal(pars[2],sd=5), - sigma = halfnormal(pars[3],sd=2)) +model <- svm(y, + rho = uniform(pars[1],-0.999,0.999), + sd_ar = halfnormal(pars[2], 5), + sigma = halfnormal(pars[3], 2)) + +# alternative parameterization +model2 <- svm(y, rho = uniform(0.98,-0.999, 0.999), + sd_ar = halfnormal(0.15, 5), mu = normal(0, 0, 1)) + +obj2 <- function(pars) { + -logLik(svm(y, + rho = uniform(pars[1], -0.999, 0.999), + sd_ar = halfnormal(pars[2], 5), + mu = normal(pars[3], 0, 1)), particles = 0) +} +opt2 <- optim(c(0.98, 0.15, 0), obj2, lower = c(-0.999, 1e-4, -Inf), + upper = c(0.999, 10, Inf), method = "L-BFGS-B") +pars2 <- opt2$par +model2 <- svm(y, + rho = uniform(pars2[1],-0.999,0.999), + sd_ar = halfnormal(pars2[2], 5), + mu = normal(pars2[3], 0, 1)) + +# sigma is internally stored in phi +ts.plot(cbind(model$phi * exp(0.5 * fast_smoother(model)), + exp(0.5 * fast_smoother(model2))), col = 1:2) } diff --git a/man/ukf.Rd b/man/ukf.Rd index 24cec35d..e7344740 100644 --- a/man/ukf.Rd +++ b/man/ukf.Rd @@ -4,22 +4,58 @@ \alias{ukf} \title{Unscented Kalman Filtering} \usage{ -ukf(model, alpha = 1, beta = 0, kappa = 2) +ukf(model, alpha = 0.001, beta = 2, kappa = 0) } \arguments{ -\item{model}{Model model} +\item{model}{Model of class \code{ssm_nlg}.} -\item{alpha, beta, kappa}{Tuning parameters for the UKF.} +\item{alpha}{Positive tuning parameter of the UKF. Default is 0.001. Smaller +the value, closer the sigma point are to the mean of the state.} + +\item{beta}{Non-negative tuning parameter of the UKF. The default value is +2, which is optimal for Gaussian states.} + +\item{kappa}{Non-negative tuning parameter of the UKF, which also affects +the spread of sigma points. Default value is 0.} } \value{ List containing the log-likelihood, one-step-ahead predictions \code{at} and filtered estimates \code{att} of states, and the corresponding variances \code{Pt} and - \code{Ptt}. +\code{Ptt}. } \description{ -Function \code{ukf} runs the unscented Kalman filter for the given -non-linear Gaussian model of class \code{ssm_nlg}, -and returns the filtered estimates and one-step-ahead predictions of the +Function \code{ukf} runs the unscented Kalman filter for the given +non-linear Gaussian model of class \code{ssm_nlg}, +and returns the filtered estimates and one-step-ahead predictions of the states \eqn{\alpha_t} given the data up to time \eqn{t}. } +\examples{ +\donttest{ # Takes a while on CRAN +set.seed(1) +mu <- -0.2 +rho <- 0.7 +sigma_y <- 0.1 +sigma_x <- 1 +x <- numeric(50) +x[1] <- rnorm(1, mu, sigma_x / sqrt(1 - rho^2)) +for(i in 2:length(x)) { + x[i] <- rnorm(1, mu * (1 - rho) + rho * x[i - 1], sigma_x) +} +y <- rnorm(50, exp(x), sigma_y) + +pntrs <- cpp_example_model("nlg_ar_exp") + +model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(mu= mu, rho = rho, + log_sigma_x = log(sigma_x), log_sigma_y = log(sigma_y)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state") + +out_iekf <- ekf(model_nlg, iekf_iter = 5) +out_ukf <- ukf(model_nlg, alpha = 0.01, beta = 2, kappa = 1) +ts.plot(cbind(x, out_iekf$att, out_ukf$att), col = 1:3) +} +} diff --git a/src/R_approx.cpp b/src/R_approx.cpp index 558a23a3..c55d693a 100644 --- a/src/R_approx.cpp +++ b/src/R_approx.cpp @@ -68,7 +68,7 @@ Rcpp::List gaussian_approx_model_nlg(const arma::mat& y, SEXP Z, SEXP H, time_varying, 1, iekf_iter, max_iter, conv_tol); model.approximate(); - if(!arma::is_finite(model.mode_estimate)) { + if(!model.mode_estimate.is_finite()) { Rcpp::warning("Approximation did not converge. "); } return Rcpp::List::create(Rcpp::Named("y") = model.approx_model.y, diff --git a/src/R_ekpf.cpp b/src/R_ekpf.cpp index 09ef18b2..74c1ad27 100644 --- a/src/R_ekpf.cpp +++ b/src/R_ekpf.cpp @@ -43,10 +43,11 @@ Rcpp::List ekpf(const arma::mat& y, SEXP Z, SEXP H, arma::cube Ptt(m, m, n + 1); filter_summary(alpha, at, att, Pt, Ptt, weights); + arma::inplace_trans(at); arma::inplace_trans(att); return Rcpp::List::create( - Rcpp::Named("att") = att, - Rcpp::Named("Ptt") = Ptt, + Rcpp::Named("at") = at, Rcpp::Named("att") = att, + Rcpp::Named("Pt") = Pt, Rcpp::Named("Ptt") = Ptt, Rcpp::Named("weights") = weights, Rcpp::Named("logLik") = loglik, Rcpp::Named("alpha") = alpha); } diff --git a/src/R_iact.cpp b/src/R_iact.cpp new file mode 100644 index 00000000..17fd70b5 --- /dev/null +++ b/src/R_iact.cpp @@ -0,0 +1,15 @@ +#include +// [[Rcpp::depends(RcppArmadillo)]] +// [[Rcpp::export]] +double IACT(const arma::vec x) { + + unsigned int n = x.n_elem; + double C = std::max(5.0, std::log10(n)); + double tau = 1.0; + + for (unsigned int k = 1; k < n; k++) { + tau += 2.0 * arma::dot(x.subvec(0, n - k - 1), x.subvec(k, n - 1)) / (n - k); + if (k > C * tau) break; + } + return std::max(0.0, tau); +} diff --git a/src/R_mcmc.cpp b/src/R_mcmc.cpp index 41b408dd..f7843bf6 100644 --- a/src/R_mcmc.cpp +++ b/src/R_mcmc.cpp @@ -15,7 +15,7 @@ Rcpp::List gaussian_mcmc(const Rcpp::List model_, const unsigned int output_type, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, - const unsigned int n_threads, const int model_type) { + const unsigned int n_threads, const int model_type, const bool verbose) { arma::vec a1 = Rcpp::as(model_["a1"]); unsigned int m = a1.n_elem; @@ -29,7 +29,7 @@ Rcpp::List gaussian_mcmc(const Rcpp::List model_, n = y.n_rows; } mcmc mcmc_run(iter, burnin, thin, n, m, - target_acceptance, gamma, S, output_type); + target_acceptance, gamma, S, output_type, verbose); switch (model_type) { case 0: { @@ -171,7 +171,8 @@ Rcpp::List nongaussian_pm_mcmc(const Rcpp::List model_, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, - const unsigned int sampling_method, const unsigned int model_type) { + const unsigned int sampling_method, const unsigned int model_type, + const bool verbose) { arma::vec a1 = Rcpp::as(model_["a1"]); unsigned int m = a1.n_elem; @@ -186,7 +187,7 @@ Rcpp::List nongaussian_pm_mcmc(const Rcpp::List model_, } mcmc mcmc_run(iter, burnin, thin, n, m, - target_acceptance, gamma, S, output_type); + target_acceptance, gamma, S, output_type, verbose); switch (model_type) { case 0: { @@ -251,7 +252,8 @@ Rcpp::List nongaussian_da_mcmc(const Rcpp::List model_, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, - const unsigned int sampling_method, const int model_type) { + const unsigned int sampling_method, const int model_type, + const bool verbose) { arma::vec a1 = Rcpp::as(model_["a1"]); unsigned int m = a1.n_elem; @@ -264,7 +266,8 @@ Rcpp::List nongaussian_da_mcmc(const Rcpp::List model_, arma::mat y = Rcpp::as(model_["y"]); n = y.n_rows; } - mcmc mcmc_run(iter, burnin, thin, n, m, target_acceptance, gamma, S, output_type); + mcmc mcmc_run(iter, burnin, thin, n, m, target_acceptance, gamma, S, + output_type, verbose); switch (model_type) { case 0: { @@ -332,7 +335,7 @@ Rcpp::List nongaussian_is_mcmc(const Rcpp::List model_, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, const unsigned int sampling_method, const unsigned int is_type, - const int model_type, const bool approx) { + const int model_type, const bool approx, const bool verbose) { arma::vec a1 = Rcpp::as(model_["a1"]); unsigned int m = a1.n_elem; @@ -348,7 +351,7 @@ Rcpp::List nongaussian_is_mcmc(const Rcpp::List model_, p = y.n_cols; } approx_mcmc mcmc_run(iter, burnin, thin, n, m, p, - target_acceptance, gamma, S, output_type, true); + target_acceptance, gamma, S, output_type, true, verbose); if (nsim <= 1) { mcmc_run.alpha_storage.zeros(); @@ -374,7 +377,6 @@ Rcpp::List nongaussian_is_mcmc(const Rcpp::List model_, if(is_type == 3) { mcmc_run.expand(); } - switch (sampling_method) { case 1: mcmc_run.is_correction_psi(model, nsim, is_type, n_threads, model_["update_fn"]); @@ -557,7 +559,7 @@ Rcpp::List nonlinear_pm_mcmc(const arma::mat& y, SEXP Z, SEXP H, const bool end_ram, const unsigned int n_threads, const unsigned int max_iter, const double conv_tol, const unsigned int sampling_method, const unsigned int iekf_iter, - const unsigned int output_type) { + const unsigned int output_type, const bool verbose) { Rcpp::XPtr xpfun_Z(Z); @@ -575,7 +577,7 @@ Rcpp::List nonlinear_pm_mcmc(const arma::mat& y, SEXP Z, SEXP H, time_varying, seed); mcmc mcmc_run(iter, burnin, thin, model.n, - model.m, target_acceptance, gamma, S, output_type); + model.m, target_acceptance, gamma, S, output_type, verbose); mcmc_run.pm_mcmc(model, sampling_method, nsim, end_ram); switch (output_type) { @@ -617,7 +619,7 @@ Rcpp::List nonlinear_da_mcmc(const arma::mat& y, SEXP Z, SEXP H, const bool end_ram, const unsigned int n_threads, const unsigned int max_iter, const double conv_tol, const unsigned int sampling_method, const unsigned int iekf_iter, - const unsigned int output_type) { + const unsigned int output_type, const bool verbose) { Rcpp::XPtr xpfun_Z(Z); @@ -635,7 +637,7 @@ Rcpp::List nonlinear_da_mcmc(const arma::mat& y, SEXP Z, SEXP H, time_varying, seed); mcmc mcmc_run(iter, burnin, thin, model.n, - model.m, target_acceptance, gamma, S, output_type); + model.m, target_acceptance, gamma, S, output_type, verbose); mcmc_run.da_mcmc(model, sampling_method, nsim, end_ram); switch (output_type) { @@ -676,7 +678,8 @@ Rcpp::List nonlinear_ekf_mcmc(const arma::mat& y, SEXP Z, SEXP H, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int n_threads, - const unsigned int iekf_iter, const unsigned int output_type) { + const unsigned int iekf_iter, const unsigned int output_type, + const bool verbose) { Rcpp::XPtr xpfun_Z(Z); @@ -694,7 +697,7 @@ Rcpp::List nonlinear_ekf_mcmc(const arma::mat& y, SEXP Z, SEXP H, time_varying, seed); approx_mcmc mcmc_run(iter, burnin, thin, model.n, - model.m, model.m, target_acceptance, gamma, S, output_type, false); + model.m, model.m, target_acceptance, gamma, S, output_type, false, verbose); mcmc_run.ekf_mcmc(model, end_ram); @@ -742,7 +745,7 @@ Rcpp::List nonlinear_is_mcmc(const arma::mat& y, SEXP Z, SEXP H, const unsigned int sampling_method, const unsigned int max_iter, const double conv_tol, const unsigned int iekf_iter, const unsigned int output_type, - const bool approx) { + const bool approx, const bool verbose) { Rcpp::XPtr xpfun_Z(Z); Rcpp::XPtr xpfun_H(H); @@ -759,9 +762,16 @@ Rcpp::List nonlinear_is_mcmc(const arma::mat& y, SEXP Z, SEXP H, time_varying, seed, iekf_iter, max_iter, conv_tol); approx_mcmc mcmc_run(iter, burnin, thin, model.n, - model.m, model.m, target_acceptance, gamma, S, output_type); + model.m, model.m, target_acceptance, gamma, S, output_type, true, verbose); + if (nsim <= 1) { + mcmc_run.alpha_storage.zeros(); + mcmc_run.weight_storage.ones(); + mcmc_run.posterior_storage.zeros(); + } + mcmc_run.amcmc(model, sampling_method, end_ram); + if(approx) { if(output_type == 1) { mcmc_run.approx_state_posterior(model, n_threads); diff --git a/src/R_postcorrection.cpp b/src/R_postcorrection.cpp index 8dc05a70..36391c3c 100644 --- a/src/R_postcorrection.cpp +++ b/src/R_postcorrection.cpp @@ -17,11 +17,10 @@ arma::vec suggest_n_nongaussian(const Rcpp::List model_, const int model_type) { arma::vec sds(candidates.n_elem); - Rcpp::Function update_fn = model_["update_fn"]; switch (model_type) { case 0: { ssm_mng model(model_, seed); - model.update_model(theta, update_fn); + model.update_model(theta, model_["update_fn"]); for(unsigned int i = 0; i < candidates.n_elem; i++) { int nsim = candidates(i); arma::cube alpha(model.m, model.n + 1, nsim); @@ -37,7 +36,7 @@ arma::vec suggest_n_nongaussian(const Rcpp::List model_, } break; case 1: { ssm_ung model(model_, seed); - model.update_model(theta, update_fn); + model.update_model(theta, model_["update_fn"]); for(unsigned int i = 0; i < candidates.n_elem; i++) { int nsim = candidates(i); arma::cube alpha(model.m, model.n + 1, nsim); @@ -53,7 +52,7 @@ arma::vec suggest_n_nongaussian(const Rcpp::List model_, } break; case 2: { bsm_ng model(model_, seed); - model.update_model(theta, update_fn); + model.update_model(theta); for(unsigned int i = 0; i < candidates.n_elem; i++) { int nsim = candidates(i); arma::cube alpha(model.m, model.n + 1, nsim); @@ -69,7 +68,7 @@ arma::vec suggest_n_nongaussian(const Rcpp::List model_, } break; case 3: { svm model(model_, seed); - model.update_model(theta, update_fn); + model.update_model(theta); for(unsigned int i = 0; i < candidates.n_elem; i++) { int nsim = candidates(i); arma::cube alpha(model.m, model.n + 1, nsim); @@ -85,7 +84,7 @@ arma::vec suggest_n_nongaussian(const Rcpp::List model_, } break; case 4: { ar1_ng model(model_, seed); - model.update_model(theta, update_fn); + model.update_model(theta); for(unsigned int i = 0; i < candidates.n_elem; i++) { int nsim = candidates(i); arma::cube alpha(model.m, model.n + 1, nsim); @@ -114,7 +113,6 @@ arma::vec suggest_n_nonlinear(const arma::mat& y, SEXP Z, SEXP H, const arma::vec theta_map, const arma::vec candidates, const unsigned int replications, const unsigned int seed) { - Rcpp::XPtr xpfun_Z(Z); Rcpp::XPtr xpfun_H(H); Rcpp::XPtr xpfun_T(T); @@ -130,7 +128,7 @@ arma::vec suggest_n_nonlinear(const arma::mat& y, SEXP Z, SEXP H, n_states, n_etas, time_varying, seed); - model.update_model(theta_map, R_NilValue); + model.update_model(theta_map); arma::vec sds(candidates.n_elem); for(unsigned int i = 0; i < candidates.n_elem; i++) { int nsim = candidates(i); @@ -174,7 +172,7 @@ Rcpp::List postcorrection_nongaussian(const Rcpp::List model_, } approx_mcmc mcmc_run(counts.n_elem, 0, 1, n, m, p, - 0.234, 1, arma::mat(theta.n_rows, theta.n_rows), output_type, true); + 0.234, 1, arma::mat(theta.n_rows, theta.n_rows), output_type, false); mcmc_run.n_stored = counts.n_elem; // mcmc_run.trim_storage(); @@ -279,7 +277,7 @@ Rcpp::List postcorrection_nonlinear(const arma::mat& y, SEXP Z, SEXP H, n_states, n_etas, time_varying, seed); approx_mcmc mcmc_run(counts.n_elem, 0, 1, model.n, model.m, model.m, - 0.234, 1, arma::mat(theta.n_rows, theta.n_rows), output_type, true); + 0.234, 1, arma::mat(theta.n_rows, theta.n_rows), output_type, false); mcmc_run.n_stored = counts.n_elem; // mcmc_run.trim_storage(); mcmc_run.count_storage = counts; diff --git a/src/R_predict.cpp b/src/R_predict.cpp index a72054a8..695a3629 100644 --- a/src/R_predict.cpp +++ b/src/R_predict.cpp @@ -15,6 +15,7 @@ arma::cube gaussian_predict(const Rcpp::List model_, const unsigned int predict_type, const unsigned int seed, const int model_type) { + // needs a deep copy via cloning in ar1 and bsm case, I don't understand why switch (model_type) { case 0: { ssm_mlg model(model_, seed); @@ -25,11 +26,11 @@ arma::cube gaussian_predict(const Rcpp::List model_, return model.predict_sample(theta, alpha, predict_type, model_["update_fn"]); } break; case 2: { - bsm_lg model(model_, seed); + bsm_lg model(Rcpp::clone(model_), seed); return model.predict_sample(theta, alpha, predict_type); } break; case 3: { - ar1_lg model(model_, seed); + ar1_lg model(Rcpp::clone(model_), seed); return model.predict_sample(theta, alpha, predict_type); } break; } @@ -42,6 +43,7 @@ arma::cube nongaussian_predict(const Rcpp::List model_, const unsigned int predict_type,const unsigned int seed, const unsigned int model_type) { + // needs a deep copy via cloning in ar1, bsm and svm cases, I don't understand why switch (model_type) { case 0: { ssm_mng model(model_, seed); @@ -52,15 +54,15 @@ arma::cube nongaussian_predict(const Rcpp::List model_, return model.predict_sample(theta, alpha, predict_type, model_["update_fn"]); } break; case 2: { - bsm_ng model(model_, seed); + bsm_ng model(Rcpp::clone(model_), seed); return model.predict_sample(theta, alpha, predict_type); } break; case 3: { - svm model(model_, seed); + svm model(Rcpp::clone(model_), seed); return model.predict_sample(theta, alpha, predict_type); } break; case 4: { - ar1_ng model(model_, seed); + ar1_ng model(Rcpp::clone(model_), seed); return model.predict_sample(theta, alpha, predict_type); } break; } diff --git a/src/R_predict_past.cpp b/src/R_predict_past.cpp index 10363a8b..210d5256 100644 --- a/src/R_predict_past.cpp +++ b/src/R_predict_past.cpp @@ -15,6 +15,7 @@ arma::cube gaussian_predict_past(const Rcpp::List model_, const unsigned int predict_type, const unsigned int seed, const int model_type) { + // needs deep copy switch (model_type) { case 0: { ssm_mlg model(model_, seed); @@ -25,11 +26,11 @@ arma::cube gaussian_predict_past(const Rcpp::List model_, return model.predict_past(theta, alpha, predict_type, model_["update_fn"]); } break; case 2: { - bsm_lg model(model_, seed); + bsm_lg model(Rcpp::clone(model_), seed); return model.predict_past(theta, alpha, predict_type); } break; case 3: { - ar1_lg model(model_, seed); + ar1_lg model(Rcpp::clone(model_), seed); return model.predict_past(theta, alpha, predict_type); } break; } @@ -52,15 +53,15 @@ arma::cube nongaussian_predict_past(const Rcpp::List model_, return model.predict_past(theta, alpha, predict_type, model_["update_fn"]); } break; case 2: { - bsm_ng model(model_, seed); + bsm_ng model(Rcpp::clone(model_), seed); return model.predict_past(theta, alpha, predict_type); } break; case 3: { - svm model(model_, seed); + svm model(Rcpp::clone(model_), seed); return model.predict_past(theta, alpha, predict_type); } break; case 4: { - ar1_ng model(model_, seed); + ar1_ng model(Rcpp::clone(model_), seed); return model.predict_past(theta, alpha, predict_type); } break; } diff --git a/src/R_psi.cpp b/src/R_psi.cpp index cde08c74..d249dbba 100644 --- a/src/R_psi.cpp +++ b/src/R_psi.cpp @@ -198,7 +198,7 @@ Rcpp::List psi_smoother_nlg(const arma::mat& y, SEXP Z, SEXP H, unsigned n = model.n; model.approximate(); - if(!arma::is_finite(model.mode_estimate)) { + if(!model.mode_estimate.is_finite()) { Rcpp::warning("Approximation did not converge. "); } arma::cube alpha(m, n + 1, nsim, arma::fill::zeros); diff --git a/src/R_sde.cpp b/src/R_sde.cpp index 7e8ba04d..9d5a5474 100644 --- a/src/R_sde.cpp +++ b/src/R_sde.cpp @@ -119,7 +119,7 @@ Rcpp::List sde_pm_mcmc(const arma::vec& y, const double x0, const unsigned int seed, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, - const bool end_ram, const unsigned int type) { + const bool end_ram, const unsigned int type, const bool verbose) { Rcpp::XPtr xpfun_drift(drift_pntr); Rcpp::XPtr xpfun_diffusion(diffusion_pntr); @@ -132,7 +132,7 @@ Rcpp::List sde_pm_mcmc(const arma::vec& y, const double x0, L, L, seed); mcmc mcmc_run(iter, burnin, - thin, model.n, 1, target_acceptance, gamma, S, type); + thin, model.n, 1, target_acceptance, gamma, S, type, verbose); mcmc_run.pm_mcmc(model, nsim, end_ram); @@ -173,7 +173,7 @@ Rcpp::List sde_da_mcmc(const arma::vec& y, const double x0, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, - const bool end_ram, const unsigned int type) { + const bool end_ram, const unsigned int type, const bool verbose) { Rcpp::XPtr xpfun_drift(drift_pntr); Rcpp::XPtr xpfun_diffusion(diffusion_pntr); @@ -186,7 +186,7 @@ Rcpp::List sde_da_mcmc(const arma::vec& y, const double x0, L_f, L_c, seed); mcmc mcmc_run(iter, burnin, - thin, model.n, 1, target_acceptance, gamma, S, type); + thin, model.n, 1, target_acceptance, gamma, S, type, verbose); mcmc_run.da_mcmc(model, nsim, end_ram); @@ -227,7 +227,7 @@ Rcpp::List sde_is_mcmc(const arma::vec& y, const double x0, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int is_type, const unsigned int n_threads, - const unsigned int type) { + const unsigned int type, const bool verbose) { Rcpp::XPtr xpfun_drift(drift_pntr); Rcpp::XPtr xpfun_diffusion(diffusion_pntr); @@ -240,7 +240,7 @@ Rcpp::List sde_is_mcmc(const arma::vec& y, const double x0, L_f, L_c, seed); approx_mcmc mcmc_run(iter, burnin, thin, model.n, 1, 1, - target_acceptance, gamma, S, type, false); + target_acceptance, gamma, S, type, false, verbose); mcmc_run.amcmc(model, nsim, end_ram); diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 355b1de9..a30b7e2f 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -1,1323 +1,1351 @@ -// Generated by using Rcpp::compileAttributes() -> do not edit by hand -// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 - -#include -#include - -using namespace Rcpp; - -// gaussian_approx_model -Rcpp::List gaussian_approx_model(const Rcpp::List model_, const int model_type); -RcppExport SEXP _bssm_gaussian_approx_model(SEXP model_SEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_approx_model(model_, model_type)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_approx_model_nlg -Rcpp::List gaussian_approx_model_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int max_iter, const double conv_tol, const unsigned int iekf_iter); -RcppExport SEXP _bssm_gaussian_approx_model_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP iekf_iterSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); - Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_approx_model_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, max_iter, conv_tol, iekf_iter)); - return rcpp_result_gen; -END_RCPP -} -// bsf -Rcpp::List bsf(const Rcpp::List model_, const unsigned int nsim, const unsigned int seed, bool gaussian, const int model_type); -RcppExport SEXP _bssm_bsf(SEXP model_SEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP gaussianSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< bool >::type gaussian(gaussianSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(bsf(model_, nsim, seed, gaussian, model_type)); - return rcpp_result_gen; -END_RCPP -} -// bsf_smoother -Rcpp::List bsf_smoother(const Rcpp::List model_, const unsigned int nsim, const unsigned int seed, bool gaussian, const int model_type); -RcppExport SEXP _bssm_bsf_smoother(SEXP model_SEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP gaussianSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< bool >::type gaussian(gaussianSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(bsf_smoother(model_, nsim, seed, gaussian, model_type)); - return rcpp_result_gen; -END_RCPP -} -// bsf_nlg -Rcpp::List bsf_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed); -RcppExport SEXP _bssm_bsf_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(bsf_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed)); - return rcpp_result_gen; -END_RCPP -} -// bsf_smoother_nlg -Rcpp::List bsf_smoother_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed); -RcppExport SEXP _bssm_bsf_smoother_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(bsf_smoother_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed)); - return rcpp_result_gen; -END_RCPP -} -// ekf_nlg -Rcpp::List ekf_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int iekf_iter); -RcppExport SEXP _bssm_ekf_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP iekf_iterSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - rcpp_result_gen = Rcpp::wrap(ekf_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter)); - return rcpp_result_gen; -END_RCPP -} -// ekf_smoother_nlg -Rcpp::List ekf_smoother_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int iekf_iter); -RcppExport SEXP _bssm_ekf_smoother_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP iekf_iterSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - rcpp_result_gen = Rcpp::wrap(ekf_smoother_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter)); - return rcpp_result_gen; -END_RCPP -} -// ekf_fast_smoother_nlg -Rcpp::List ekf_fast_smoother_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int iekf_iter); -RcppExport SEXP _bssm_ekf_fast_smoother_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP iekf_iterSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - rcpp_result_gen = Rcpp::wrap(ekf_fast_smoother_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter)); - return rcpp_result_gen; -END_RCPP -} -// ekpf -Rcpp::List ekpf(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed); -RcppExport SEXP _bssm_ekpf(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(ekpf(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed)); - return rcpp_result_gen; -END_RCPP -} -// ekpf_smoother -Rcpp::List ekpf_smoother(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed); -RcppExport SEXP _bssm_ekpf_smoother(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(ekpf_smoother(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed)); - return rcpp_result_gen; -END_RCPP -} -// importance_sample_ng -Rcpp::List importance_sample_ng(const Rcpp::List model_, unsigned int nsim, bool use_antithetic, const unsigned int seed, const int model_type); -RcppExport SEXP _bssm_importance_sample_ng(SEXP model_SEXP, SEXP nsimSEXP, SEXP use_antitheticSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< bool >::type use_antithetic(use_antitheticSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(importance_sample_ng(model_, nsim, use_antithetic, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_kfilter -Rcpp::List gaussian_kfilter(const Rcpp::List model_, const unsigned int model_type); -RcppExport SEXP _bssm_gaussian_kfilter(SEXP model_SEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_kfilter(model_, model_type)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_loglik -double gaussian_loglik(const Rcpp::List model_, const int model_type); -RcppExport SEXP _bssm_gaussian_loglik(SEXP model_SEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_loglik(model_, model_type)); - return rcpp_result_gen; -END_RCPP -} -// nongaussian_loglik -double nongaussian_loglik(const Rcpp::List model_, const unsigned int nsim, const unsigned int sampling_method, const unsigned int seed, const int model_type); -RcppExport SEXP _bssm_nongaussian_loglik(SEXP model_SEXP, SEXP nsimSEXP, SEXP sampling_methodSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(nongaussian_loglik(model_, nsim, sampling_method, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// nonlinear_loglik -double nonlinear_loglik(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed, const unsigned int max_iter, const double conv_tol, const unsigned int iekf_iter, const unsigned int method); -RcppExport SEXP _bssm_nonlinear_loglik(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP iekf_iterSEXP, SEXP methodSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); - Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type method(methodSEXP); - rcpp_result_gen = Rcpp::wrap(nonlinear_loglik(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter, method)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_mcmc -Rcpp::List gaussian_mcmc(const Rcpp::List model_, const unsigned int output_type, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, const int model_type); -RcppExport SEXP _bssm_gaussian_mcmc(SEXP model_SEXP, SEXP output_typeSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP seedSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_mcmc(model_, output_type, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, model_type)); - return rcpp_result_gen; -END_RCPP -} -// nongaussian_pm_mcmc -Rcpp::List nongaussian_pm_mcmc(const Rcpp::List model_, const unsigned int output_type, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, const unsigned int sampling_method, const unsigned int model_type); -RcppExport SEXP _bssm_nongaussian_pm_mcmc(SEXP model_SEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP seedSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP sampling_methodSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(nongaussian_pm_mcmc(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type)); - return rcpp_result_gen; -END_RCPP -} -// nongaussian_da_mcmc -Rcpp::List nongaussian_da_mcmc(const Rcpp::List model_, const unsigned int output_type, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, const unsigned int sampling_method, const int model_type); -RcppExport SEXP _bssm_nongaussian_da_mcmc(SEXP model_SEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP seedSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP sampling_methodSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(nongaussian_da_mcmc(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type)); - return rcpp_result_gen; -END_RCPP -} -// nongaussian_is_mcmc -Rcpp::List nongaussian_is_mcmc(const Rcpp::List model_, const unsigned int output_type, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, const unsigned int sampling_method, const unsigned int is_type, const int model_type, const bool approx); -RcppExport SEXP _bssm_nongaussian_is_mcmc(SEXP model_SEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP seedSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP sampling_methodSEXP, SEXP is_typeSEXP, SEXP model_typeSEXP, SEXP approxSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - Rcpp::traits::input_parameter< const bool >::type approx(approxSEXP); - rcpp_result_gen = Rcpp::wrap(nongaussian_is_mcmc(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, is_type, model_type, approx)); - return rcpp_result_gen; -END_RCPP -} -// nonlinear_pm_mcmc -Rcpp::List nonlinear_pm_mcmc(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const unsigned int seed, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int n_threads, const unsigned int max_iter, const double conv_tol, const unsigned int sampling_method, const unsigned int iekf_iter, const unsigned int output_type); -RcppExport SEXP _bssm_nonlinear_pm_mcmc(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP seedSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP sampling_methodSEXP, SEXP iekf_iterSEXP, SEXP output_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); - Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - rcpp_result_gen = Rcpp::wrap(nonlinear_pm_mcmc(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type)); - return rcpp_result_gen; -END_RCPP -} -// nonlinear_da_mcmc -Rcpp::List nonlinear_da_mcmc(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const unsigned int seed, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int n_threads, const unsigned int max_iter, const double conv_tol, const unsigned int sampling_method, const unsigned int iekf_iter, const unsigned int output_type); -RcppExport SEXP _bssm_nonlinear_da_mcmc(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP seedSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP sampling_methodSEXP, SEXP iekf_iterSEXP, SEXP output_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); - Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - rcpp_result_gen = Rcpp::wrap(nonlinear_da_mcmc(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type)); - return rcpp_result_gen; -END_RCPP -} -// nonlinear_ekf_mcmc -Rcpp::List nonlinear_ekf_mcmc(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const unsigned int seed, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int n_threads, const unsigned int iekf_iter, const unsigned int output_type); -RcppExport SEXP _bssm_nonlinear_ekf_mcmc(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP seedSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP iekf_iterSEXP, SEXP output_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - rcpp_result_gen = Rcpp::wrap(nonlinear_ekf_mcmc(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, iekf_iter, output_type)); - return rcpp_result_gen; -END_RCPP -} -// nonlinear_is_mcmc -Rcpp::List nonlinear_is_mcmc(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const unsigned int seed, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int n_threads, const unsigned int is_type, const unsigned int sampling_method, const unsigned int max_iter, const double conv_tol, const unsigned int iekf_iter, const unsigned int output_type, const bool approx); -RcppExport SEXP _bssm_nonlinear_is_mcmc(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP seedSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP is_typeSEXP, SEXP sampling_methodSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP iekf_iterSEXP, SEXP output_typeSEXP, SEXP approxSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); - Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - Rcpp::traits::input_parameter< const bool >::type approx(approxSEXP); - rcpp_result_gen = Rcpp::wrap(nonlinear_is_mcmc(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, is_type, sampling_method, max_iter, conv_tol, iekf_iter, output_type, approx)); - return rcpp_result_gen; -END_RCPP -} -// R_milstein -double R_milstein(const double x0, const unsigned int L, const double t, const arma::vec& theta, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, bool positive, const unsigned int seed); -RcppExport SEXP _bssm_R_milstein(SEXP x0SEXP, SEXP LSEXP, SEXP tSEXP, SEXP thetaSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP positiveSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); - Rcpp::traits::input_parameter< const double >::type t(tSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); - Rcpp::traits::input_parameter< bool >::type positive(positiveSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(R_milstein(x0, L, t, theta, drift_pntr, diffusion_pntr, ddiffusion_pntr, positive, seed)); - return rcpp_result_gen; -END_RCPP -} -// suggest_n_nongaussian -arma::vec suggest_n_nongaussian(const Rcpp::List model_, const arma::vec theta, const arma::vec candidates, const unsigned int replications, const unsigned int seed, const int model_type); -RcppExport SEXP _bssm_suggest_n_nongaussian(SEXP model_SEXP, SEXP thetaSEXP, SEXP candidatesSEXP, SEXP replicationsSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const arma::vec >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const arma::vec >::type candidates(candidatesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type replications(replicationsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(suggest_n_nongaussian(model_, theta, candidates, replications, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// suggest_n_nonlinear -arma::vec suggest_n_nonlinear(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const arma::vec theta_map, const arma::vec candidates, const unsigned int replications, const unsigned int seed); -RcppExport SEXP _bssm_suggest_n_nonlinear(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP theta_mapSEXP, SEXP candidatesSEXP, SEXP replicationsSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const arma::vec >::type theta_map(theta_mapSEXP); - Rcpp::traits::input_parameter< const arma::vec >::type candidates(candidatesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type replications(replicationsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(suggest_n_nonlinear(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, theta_map, candidates, replications, seed)); - return rcpp_result_gen; -END_RCPP -} -// postcorrection_nongaussian -Rcpp::List postcorrection_nongaussian(const Rcpp::List model_, const int model_type, const unsigned int output_type, const unsigned int nsim, const unsigned int seed, const unsigned int n_threads, const unsigned int is_type, const arma::uvec counts, const arma::mat theta, const arma::cube modes); -RcppExport SEXP _bssm_postcorrection_nongaussian(SEXP model_SEXP, SEXP model_typeSEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP n_threadsSEXP, SEXP is_typeSEXP, SEXP countsSEXP, SEXP thetaSEXP, SEXP modesSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); - Rcpp::traits::input_parameter< const arma::uvec >::type counts(countsSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const arma::cube >::type modes(modesSEXP); - rcpp_result_gen = Rcpp::wrap(postcorrection_nongaussian(model_, model_type, output_type, nsim, seed, n_threads, is_type, counts, theta, modes)); - return rcpp_result_gen; -END_RCPP -} -// postcorrection_nonlinear -Rcpp::List postcorrection_nonlinear(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta_init, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int output_type, const unsigned int nsim, const unsigned int seed, const unsigned int n_threads, const unsigned int is_type, const arma::uvec counts, const arma::mat theta, const arma::cube modes); -RcppExport SEXP _bssm_postcorrection_nonlinear(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP theta_initSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP n_threadsSEXP, SEXP is_typeSEXP, SEXP countsSEXP, SEXP thetaSEXP, SEXP modesSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta_init(theta_initSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); - Rcpp::traits::input_parameter< const arma::uvec >::type counts(countsSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const arma::cube >::type modes(modesSEXP); - rcpp_result_gen = Rcpp::wrap(postcorrection_nonlinear(y, Z, H, T, R, Zg, Tg, a1, P1, theta_init, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, output_type, nsim, seed, n_threads, is_type, counts, theta, modes)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_predict -arma::cube gaussian_predict(const Rcpp::List model_, const arma::mat theta, const arma::mat alpha, const unsigned int predict_type, const unsigned int seed, const int model_type); -RcppExport SEXP _bssm_gaussian_predict(SEXP model_SEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const arma::mat >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type alpha(alphaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_predict(model_, theta, alpha, predict_type, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// nongaussian_predict -arma::cube nongaussian_predict(const Rcpp::List model_, const arma::mat& theta, const arma::mat& alpha, const unsigned int predict_type, const unsigned int seed, const unsigned int model_type); -RcppExport SEXP _bssm_nongaussian_predict(SEXP model_SEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type alpha(alphaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(nongaussian_predict(model_, theta, alpha, predict_type, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// nonlinear_predict -arma::cube nonlinear_predict(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const arma::mat& theta, const arma::mat& alpha, const unsigned int predict_type, const unsigned int seed); -RcppExport SEXP _bssm_nonlinear_predict(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type alpha(alphaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(nonlinear_predict(y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_predict_past -arma::cube gaussian_predict_past(const Rcpp::List model_, const arma::mat& theta, const arma::cube& alpha, const unsigned int predict_type, const unsigned int seed, const int model_type); -RcppExport SEXP _bssm_gaussian_predict_past(SEXP model_SEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const arma::cube& >::type alpha(alphaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_predict_past(model_, theta, alpha, predict_type, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// nongaussian_predict_past -arma::cube nongaussian_predict_past(const Rcpp::List model_, const arma::mat& theta, const arma::cube& alpha, const unsigned int predict_type, const unsigned int seed, const unsigned int model_type); -RcppExport SEXP _bssm_nongaussian_predict_past(SEXP model_SEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const arma::cube& >::type alpha(alphaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(nongaussian_predict_past(model_, theta, alpha, predict_type, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// nonlinear_predict_past -arma::cube nonlinear_predict_past(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const arma::mat& theta, const arma::cube& alpha, const unsigned int predict_type, const unsigned int seed); -RcppExport SEXP _bssm_nonlinear_predict_past(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const arma::cube& >::type alpha(alphaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(nonlinear_predict_past(y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_psi_smoother -arma::cube gaussian_psi_smoother(const Rcpp::List model_, const unsigned int nsim, const unsigned int seed, const int model_type); -RcppExport SEXP _bssm_gaussian_psi_smoother(SEXP model_SEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_psi_smoother(model_, nsim, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// psi_smoother -Rcpp::List psi_smoother(const Rcpp::List model_, const unsigned int nsim, const unsigned int seed, const int model_type); -RcppExport SEXP _bssm_psi_smoother(SEXP model_SEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(psi_smoother(model_, nsim, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// psi_smoother_nlg -Rcpp::List psi_smoother_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed, const unsigned int max_iter, const double conv_tol, const unsigned int iekf_iter); -RcppExport SEXP _bssm_psi_smoother_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP iekf_iterSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); - Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); - rcpp_result_gen = Rcpp::wrap(psi_smoother_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter)); - return rcpp_result_gen; -END_RCPP -} -// loglik_sde -double loglik_sde(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L, const unsigned int seed); -RcppExport SEXP _bssm_loglik_sde(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP LSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); - Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); - Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); - Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(loglik_sde(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed)); - return rcpp_result_gen; -END_RCPP -} -// bsf_sde -Rcpp::List bsf_sde(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L, const unsigned int seed); -RcppExport SEXP _bssm_bsf_sde(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP LSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); - Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); - Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); - Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(bsf_sde(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed)); - return rcpp_result_gen; -END_RCPP -} -// bsf_smoother_sde -Rcpp::List bsf_smoother_sde(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L, const unsigned int seed); -RcppExport SEXP _bssm_bsf_smoother_sde(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP LSEXP, SEXP seedSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); - Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); - Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); - Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(bsf_smoother_sde(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed)); - return rcpp_result_gen; -END_RCPP -} -// sde_pm_mcmc -Rcpp::List sde_pm_mcmc(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L, const unsigned int seed, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int type); -RcppExport SEXP _bssm_sde_pm_mcmc(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP LSEXP, SEXP seedSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); - Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); - Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); - Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type type(typeSEXP); - rcpp_result_gen = Rcpp::wrap(sde_pm_mcmc(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type)); - return rcpp_result_gen; -END_RCPP -} -// sde_da_mcmc -Rcpp::List sde_da_mcmc(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L_c, const unsigned int L_f, const unsigned int seed, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int type); -RcppExport SEXP _bssm_sde_da_mcmc(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP L_cSEXP, SEXP L_fSEXP, SEXP seedSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); - Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); - Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); - Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L_c(L_cSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L_f(L_fSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type type(typeSEXP); - rcpp_result_gen = Rcpp::wrap(sde_da_mcmc(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type)); - return rcpp_result_gen; -END_RCPP -} -// sde_is_mcmc -Rcpp::List sde_is_mcmc(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L_c, const unsigned int L_f, const unsigned int seed, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int is_type, const unsigned int n_threads, const unsigned int type); -RcppExport SEXP _bssm_sde_is_mcmc(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP L_cSEXP, SEXP L_fSEXP, SEXP seedSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP is_typeSEXP, SEXP n_threadsSEXP, SEXP typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); - Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); - Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); - Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L_c(L_cSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L_f(L_fSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); - Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); - Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); - Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); - Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type type(typeSEXP); - rcpp_result_gen = Rcpp::wrap(sde_is_mcmc(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, is_type, n_threads, type)); - return rcpp_result_gen; -END_RCPP -} -// sde_state_sampler_bsf_is2 -Rcpp::List sde_state_sampler_bsf_is2(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const unsigned int nsim, const unsigned int L_f, const unsigned int seed, const arma::vec& approx_loglik_storage, const arma::mat& theta); -RcppExport SEXP _bssm_sde_state_sampler_bsf_is2(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP nsimSEXP, SEXP L_fSEXP, SEXP seedSEXP, SEXP approx_loglik_storageSEXP, SEXP thetaSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); - Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); - Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); - Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type L_f(L_fSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type approx_loglik_storage(approx_loglik_storageSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); - rcpp_result_gen = Rcpp::wrap(sde_state_sampler_bsf_is2(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, nsim, L_f, seed, approx_loglik_storage, theta)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_smoother -Rcpp::List gaussian_smoother(const Rcpp::List model_, const int model_type); -RcppExport SEXP _bssm_gaussian_smoother(SEXP model_SEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_smoother(model_, model_type)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_ccov_smoother -Rcpp::List gaussian_ccov_smoother(const Rcpp::List model_, const int model_type); -RcppExport SEXP _bssm_gaussian_ccov_smoother(SEXP model_SEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_ccov_smoother(model_, model_type)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_fast_smoother -arma::mat gaussian_fast_smoother(const Rcpp::List model_, const int model_type); -RcppExport SEXP _bssm_gaussian_fast_smoother(SEXP model_SEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_fast_smoother(model_, model_type)); - return rcpp_result_gen; -END_RCPP -} -// gaussian_sim_smoother -arma::cube gaussian_sim_smoother(const Rcpp::List model_, const unsigned int nsim, bool use_antithetic, const unsigned int seed, const int model_type); -RcppExport SEXP _bssm_gaussian_sim_smoother(SEXP model_SEXP, SEXP nsimSEXP, SEXP use_antitheticSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); - Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); - Rcpp::traits::input_parameter< bool >::type use_antithetic(use_antitheticSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); - Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); - rcpp_result_gen = Rcpp::wrap(gaussian_sim_smoother(model_, nsim, use_antithetic, seed, model_type)); - return rcpp_result_gen; -END_RCPP -} -// ukf_nlg -Rcpp::List ukf_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const double alpha, const double beta, const double kappa); -RcppExport SEXP _bssm_ukf_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP alphaSEXP, SEXP betaSEXP, SEXP kappaSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); - Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); - Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); - Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); - Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); - Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); - Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); - Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); - Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); - Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); - Rcpp::traits::input_parameter< const double >::type alpha(alphaSEXP); - Rcpp::traits::input_parameter< const double >::type beta(betaSEXP); - Rcpp::traits::input_parameter< const double >::type kappa(kappaSEXP); - rcpp_result_gen = Rcpp::wrap(ukf_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, alpha, beta, kappa)); - return rcpp_result_gen; -END_RCPP -} -// conditional_cov -void conditional_cov(arma::cube& Vt, arma::cube& Ct, const bool use_svd); -RcppExport SEXP _bssm_conditional_cov(SEXP VtSEXP, SEXP CtSEXP, SEXP use_svdSEXP) { -BEGIN_RCPP - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< arma::cube& >::type Vt(VtSEXP); - Rcpp::traits::input_parameter< arma::cube& >::type Ct(CtSEXP); - Rcpp::traits::input_parameter< const bool >::type use_svd(use_svdSEXP); - conditional_cov(Vt, Ct, use_svd); - return R_NilValue; -END_RCPP -} -// dmvnorm -double dmvnorm(const arma::vec& x, const arma::vec& mean, const arma::mat& sigma, bool lwr, bool logd); -RcppExport SEXP _bssm_dmvnorm(SEXP xSEXP, SEXP meanSEXP, SEXP sigmaSEXP, SEXP lwrSEXP, SEXP logdSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::vec& >::type x(xSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type mean(meanSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type sigma(sigmaSEXP); - Rcpp::traits::input_parameter< bool >::type lwr(lwrSEXP); - Rcpp::traits::input_parameter< bool >::type logd(logdSEXP); - rcpp_result_gen = Rcpp::wrap(dmvnorm(x, mean, sigma, lwr, logd)); - return rcpp_result_gen; -END_RCPP -} -// precompute_dmvnorm -double precompute_dmvnorm(const arma::mat& sigma, arma::mat& Linv, const arma::uvec& nonzero); -RcppExport SEXP _bssm_precompute_dmvnorm(SEXP sigmaSEXP, SEXP LinvSEXP, SEXP nonzeroSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type sigma(sigmaSEXP); - Rcpp::traits::input_parameter< arma::mat& >::type Linv(LinvSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type nonzero(nonzeroSEXP); - rcpp_result_gen = Rcpp::wrap(precompute_dmvnorm(sigma, Linv, nonzero)); - return rcpp_result_gen; -END_RCPP -} -// fast_dmvnorm -double fast_dmvnorm(const arma::vec& x, const arma::vec& mean, const arma::mat& Linv, const arma::uvec& nonzero, const double constant); -RcppExport SEXP _bssm_fast_dmvnorm(SEXP xSEXP, SEXP meanSEXP, SEXP LinvSEXP, SEXP nonzeroSEXP, SEXP constantSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::vec& >::type x(xSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type mean(meanSEXP); - Rcpp::traits::input_parameter< const arma::mat& >::type Linv(LinvSEXP); - Rcpp::traits::input_parameter< const arma::uvec& >::type nonzero(nonzeroSEXP); - Rcpp::traits::input_parameter< const double >::type constant(constantSEXP); - rcpp_result_gen = Rcpp::wrap(fast_dmvnorm(x, mean, Linv, nonzero, constant)); - return rcpp_result_gen; -END_RCPP -} -// psd_chol -arma::mat psd_chol(const arma::mat& x); -RcppExport SEXP _bssm_psd_chol(SEXP xSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const arma::mat& >::type x(xSEXP); - rcpp_result_gen = Rcpp::wrap(psd_chol(x)); - return rcpp_result_gen; -END_RCPP -} -// stratified_sample -arma::uvec stratified_sample(arma::vec& p, const arma::vec& r, const unsigned int N); -RcppExport SEXP _bssm_stratified_sample(SEXP pSEXP, SEXP rSEXP, SEXP NSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< arma::vec& >::type p(pSEXP); - Rcpp::traits::input_parameter< const arma::vec& >::type r(rSEXP); - Rcpp::traits::input_parameter< const unsigned int >::type N(NSEXP); - rcpp_result_gen = Rcpp::wrap(stratified_sample(p, r, N)); - return rcpp_result_gen; -END_RCPP -} - -static const R_CallMethodDef CallEntries[] = { - {"_bssm_gaussian_approx_model", (DL_FUNC) &_bssm_gaussian_approx_model, 2}, - {"_bssm_gaussian_approx_model_nlg", (DL_FUNC) &_bssm_gaussian_approx_model_nlg, 19}, - {"_bssm_bsf", (DL_FUNC) &_bssm_bsf, 5}, - {"_bssm_bsf_smoother", (DL_FUNC) &_bssm_bsf_smoother, 5}, - {"_bssm_bsf_nlg", (DL_FUNC) &_bssm_bsf_nlg, 18}, - {"_bssm_bsf_smoother_nlg", (DL_FUNC) &_bssm_bsf_smoother_nlg, 18}, - {"_bssm_ekf_nlg", (DL_FUNC) &_bssm_ekf_nlg, 17}, - {"_bssm_ekf_smoother_nlg", (DL_FUNC) &_bssm_ekf_smoother_nlg, 17}, - {"_bssm_ekf_fast_smoother_nlg", (DL_FUNC) &_bssm_ekf_fast_smoother_nlg, 17}, - {"_bssm_ekpf", (DL_FUNC) &_bssm_ekpf, 18}, - {"_bssm_ekpf_smoother", (DL_FUNC) &_bssm_ekpf_smoother, 18}, - {"_bssm_importance_sample_ng", (DL_FUNC) &_bssm_importance_sample_ng, 5}, - {"_bssm_gaussian_kfilter", (DL_FUNC) &_bssm_gaussian_kfilter, 2}, - {"_bssm_gaussian_loglik", (DL_FUNC) &_bssm_gaussian_loglik, 2}, - {"_bssm_nongaussian_loglik", (DL_FUNC) &_bssm_nongaussian_loglik, 5}, - {"_bssm_nonlinear_loglik", (DL_FUNC) &_bssm_nonlinear_loglik, 22}, - {"_bssm_gaussian_mcmc", (DL_FUNC) &_bssm_gaussian_mcmc, 12}, - {"_bssm_nongaussian_pm_mcmc", (DL_FUNC) &_bssm_nongaussian_pm_mcmc, 14}, - {"_bssm_nongaussian_da_mcmc", (DL_FUNC) &_bssm_nongaussian_da_mcmc, 14}, - {"_bssm_nongaussian_is_mcmc", (DL_FUNC) &_bssm_nongaussian_is_mcmc, 16}, - {"_bssm_nonlinear_pm_mcmc", (DL_FUNC) &_bssm_nonlinear_pm_mcmc, 31}, - {"_bssm_nonlinear_da_mcmc", (DL_FUNC) &_bssm_nonlinear_da_mcmc, 31}, - {"_bssm_nonlinear_ekf_mcmc", (DL_FUNC) &_bssm_nonlinear_ekf_mcmc, 27}, - {"_bssm_nonlinear_is_mcmc", (DL_FUNC) &_bssm_nonlinear_is_mcmc, 33}, - {"_bssm_R_milstein", (DL_FUNC) &_bssm_R_milstein, 9}, - {"_bssm_suggest_n_nongaussian", (DL_FUNC) &_bssm_suggest_n_nongaussian, 6}, - {"_bssm_suggest_n_nonlinear", (DL_FUNC) &_bssm_suggest_n_nonlinear, 20}, - {"_bssm_postcorrection_nongaussian", (DL_FUNC) &_bssm_postcorrection_nongaussian, 10}, - {"_bssm_postcorrection_nonlinear", (DL_FUNC) &_bssm_postcorrection_nonlinear, 24}, - {"_bssm_gaussian_predict", (DL_FUNC) &_bssm_gaussian_predict, 6}, - {"_bssm_nongaussian_predict", (DL_FUNC) &_bssm_nongaussian_predict, 6}, - {"_bssm_nonlinear_predict", (DL_FUNC) &_bssm_nonlinear_predict, 19}, - {"_bssm_gaussian_predict_past", (DL_FUNC) &_bssm_gaussian_predict_past, 6}, - {"_bssm_nongaussian_predict_past", (DL_FUNC) &_bssm_nongaussian_predict_past, 6}, - {"_bssm_nonlinear_predict_past", (DL_FUNC) &_bssm_nonlinear_predict_past, 19}, - {"_bssm_gaussian_psi_smoother", (DL_FUNC) &_bssm_gaussian_psi_smoother, 4}, - {"_bssm_psi_smoother", (DL_FUNC) &_bssm_psi_smoother, 4}, - {"_bssm_psi_smoother_nlg", (DL_FUNC) &_bssm_psi_smoother_nlg, 21}, - {"_bssm_loglik_sde", (DL_FUNC) &_bssm_loglik_sde, 12}, - {"_bssm_bsf_sde", (DL_FUNC) &_bssm_bsf_sde, 12}, - {"_bssm_bsf_smoother_sde", (DL_FUNC) &_bssm_bsf_smoother_sde, 12}, - {"_bssm_sde_pm_mcmc", (DL_FUNC) &_bssm_sde_pm_mcmc, 20}, - {"_bssm_sde_da_mcmc", (DL_FUNC) &_bssm_sde_da_mcmc, 21}, - {"_bssm_sde_is_mcmc", (DL_FUNC) &_bssm_sde_is_mcmc, 23}, - {"_bssm_sde_state_sampler_bsf_is2", (DL_FUNC) &_bssm_sde_state_sampler_bsf_is2, 13}, - {"_bssm_gaussian_smoother", (DL_FUNC) &_bssm_gaussian_smoother, 2}, - {"_bssm_gaussian_ccov_smoother", (DL_FUNC) &_bssm_gaussian_ccov_smoother, 2}, - {"_bssm_gaussian_fast_smoother", (DL_FUNC) &_bssm_gaussian_fast_smoother, 2}, - {"_bssm_gaussian_sim_smoother", (DL_FUNC) &_bssm_gaussian_sim_smoother, 5}, - {"_bssm_ukf_nlg", (DL_FUNC) &_bssm_ukf_nlg, 19}, - {"_bssm_conditional_cov", (DL_FUNC) &_bssm_conditional_cov, 3}, - {"_bssm_dmvnorm", (DL_FUNC) &_bssm_dmvnorm, 5}, - {"_bssm_precompute_dmvnorm", (DL_FUNC) &_bssm_precompute_dmvnorm, 3}, - {"_bssm_fast_dmvnorm", (DL_FUNC) &_bssm_fast_dmvnorm, 5}, - {"_bssm_psd_chol", (DL_FUNC) &_bssm_psd_chol, 1}, - {"_bssm_stratified_sample", (DL_FUNC) &_bssm_stratified_sample, 3}, - {NULL, NULL, 0} -}; - -RcppExport void R_init_bssm(DllInfo *dll) { - R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); - R_useDynamicSymbols(dll, FALSE); -} +// Generated by using Rcpp::compileAttributes() -> do not edit by hand +// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +#include +#include + +using namespace Rcpp; + +#ifdef RCPP_USE_GLOBAL_ROSTREAM +Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); +Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); +#endif + +// gaussian_approx_model +Rcpp::List gaussian_approx_model(const Rcpp::List model_, const int model_type); +RcppExport SEXP _bssm_gaussian_approx_model(SEXP model_SEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_approx_model(model_, model_type)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_approx_model_nlg +Rcpp::List gaussian_approx_model_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int max_iter, const double conv_tol, const unsigned int iekf_iter); +RcppExport SEXP _bssm_gaussian_approx_model_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP iekf_iterSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); + Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_approx_model_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, max_iter, conv_tol, iekf_iter)); + return rcpp_result_gen; +END_RCPP +} +// bsf +Rcpp::List bsf(const Rcpp::List model_, const unsigned int nsim, const unsigned int seed, bool gaussian, const int model_type); +RcppExport SEXP _bssm_bsf(SEXP model_SEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP gaussianSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< bool >::type gaussian(gaussianSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(bsf(model_, nsim, seed, gaussian, model_type)); + return rcpp_result_gen; +END_RCPP +} +// bsf_smoother +Rcpp::List bsf_smoother(const Rcpp::List model_, const unsigned int nsim, const unsigned int seed, bool gaussian, const int model_type); +RcppExport SEXP _bssm_bsf_smoother(SEXP model_SEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP gaussianSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< bool >::type gaussian(gaussianSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(bsf_smoother(model_, nsim, seed, gaussian, model_type)); + return rcpp_result_gen; +END_RCPP +} +// bsf_nlg +Rcpp::List bsf_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed); +RcppExport SEXP _bssm_bsf_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(bsf_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed)); + return rcpp_result_gen; +END_RCPP +} +// bsf_smoother_nlg +Rcpp::List bsf_smoother_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed); +RcppExport SEXP _bssm_bsf_smoother_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(bsf_smoother_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed)); + return rcpp_result_gen; +END_RCPP +} +// ekf_nlg +Rcpp::List ekf_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int iekf_iter); +RcppExport SEXP _bssm_ekf_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP iekf_iterSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + rcpp_result_gen = Rcpp::wrap(ekf_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter)); + return rcpp_result_gen; +END_RCPP +} +// ekf_smoother_nlg +Rcpp::List ekf_smoother_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int iekf_iter); +RcppExport SEXP _bssm_ekf_smoother_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP iekf_iterSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + rcpp_result_gen = Rcpp::wrap(ekf_smoother_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter)); + return rcpp_result_gen; +END_RCPP +} +// ekf_fast_smoother_nlg +Rcpp::List ekf_fast_smoother_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int iekf_iter); +RcppExport SEXP _bssm_ekf_fast_smoother_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP iekf_iterSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + rcpp_result_gen = Rcpp::wrap(ekf_fast_smoother_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, iekf_iter)); + return rcpp_result_gen; +END_RCPP +} +// ekpf +Rcpp::List ekpf(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed); +RcppExport SEXP _bssm_ekpf(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(ekpf(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed)); + return rcpp_result_gen; +END_RCPP +} +// ekpf_smoother +Rcpp::List ekpf_smoother(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed); +RcppExport SEXP _bssm_ekpf_smoother(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(ekpf_smoother(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed)); + return rcpp_result_gen; +END_RCPP +} +// IACT +double IACT(const arma::vec x); +RcppExport SEXP _bssm_IACT(SEXP xSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec >::type x(xSEXP); + rcpp_result_gen = Rcpp::wrap(IACT(x)); + return rcpp_result_gen; +END_RCPP +} +// importance_sample_ng +Rcpp::List importance_sample_ng(const Rcpp::List model_, unsigned int nsim, bool use_antithetic, const unsigned int seed, const int model_type); +RcppExport SEXP _bssm_importance_sample_ng(SEXP model_SEXP, SEXP nsimSEXP, SEXP use_antitheticSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< bool >::type use_antithetic(use_antitheticSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(importance_sample_ng(model_, nsim, use_antithetic, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_kfilter +Rcpp::List gaussian_kfilter(const Rcpp::List model_, const unsigned int model_type); +RcppExport SEXP _bssm_gaussian_kfilter(SEXP model_SEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_kfilter(model_, model_type)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_loglik +double gaussian_loglik(const Rcpp::List model_, const int model_type); +RcppExport SEXP _bssm_gaussian_loglik(SEXP model_SEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_loglik(model_, model_type)); + return rcpp_result_gen; +END_RCPP +} +// nongaussian_loglik +double nongaussian_loglik(const Rcpp::List model_, const unsigned int nsim, const unsigned int sampling_method, const unsigned int seed, const int model_type); +RcppExport SEXP _bssm_nongaussian_loglik(SEXP model_SEXP, SEXP nsimSEXP, SEXP sampling_methodSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(nongaussian_loglik(model_, nsim, sampling_method, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// nonlinear_loglik +double nonlinear_loglik(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed, const unsigned int max_iter, const double conv_tol, const unsigned int iekf_iter, const unsigned int method); +RcppExport SEXP _bssm_nonlinear_loglik(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP iekf_iterSEXP, SEXP methodSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); + Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type method(methodSEXP); + rcpp_result_gen = Rcpp::wrap(nonlinear_loglik(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter, method)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_mcmc +Rcpp::List gaussian_mcmc(const Rcpp::List model_, const unsigned int output_type, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, const int model_type, const bool verbose); +RcppExport SEXP _bssm_gaussian_mcmc(SEXP model_SEXP, SEXP output_typeSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP seedSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP model_typeSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_mcmc(model_, output_type, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, model_type, verbose)); + return rcpp_result_gen; +END_RCPP +} +// nongaussian_pm_mcmc +Rcpp::List nongaussian_pm_mcmc(const Rcpp::List model_, const unsigned int output_type, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, const unsigned int sampling_method, const unsigned int model_type, const bool verbose); +RcppExport SEXP _bssm_nongaussian_pm_mcmc(SEXP model_SEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP seedSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP sampling_methodSEXP, SEXP model_typeSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type model_type(model_typeSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(nongaussian_pm_mcmc(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type, verbose)); + return rcpp_result_gen; +END_RCPP +} +// nongaussian_da_mcmc +Rcpp::List nongaussian_da_mcmc(const Rcpp::List model_, const unsigned int output_type, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, const unsigned int sampling_method, const int model_type, const bool verbose); +RcppExport SEXP _bssm_nongaussian_da_mcmc(SEXP model_SEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP seedSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP sampling_methodSEXP, SEXP model_typeSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(nongaussian_da_mcmc(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, model_type, verbose)); + return rcpp_result_gen; +END_RCPP +} +// nongaussian_is_mcmc +Rcpp::List nongaussian_is_mcmc(const Rcpp::List model_, const unsigned int output_type, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const unsigned int seed, const bool end_ram, const unsigned int n_threads, const unsigned int sampling_method, const unsigned int is_type, const int model_type, const bool approx, const bool verbose); +RcppExport SEXP _bssm_nongaussian_is_mcmc(SEXP model_SEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP seedSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP sampling_methodSEXP, SEXP is_typeSEXP, SEXP model_typeSEXP, SEXP approxSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + Rcpp::traits::input_parameter< const bool >::type approx(approxSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(nongaussian_is_mcmc(model_, output_type, nsim, iter, burnin, thin, gamma, target_acceptance, S, seed, end_ram, n_threads, sampling_method, is_type, model_type, approx, verbose)); + return rcpp_result_gen; +END_RCPP +} +// nonlinear_pm_mcmc +Rcpp::List nonlinear_pm_mcmc(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const unsigned int seed, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int n_threads, const unsigned int max_iter, const double conv_tol, const unsigned int sampling_method, const unsigned int iekf_iter, const unsigned int output_type, const bool verbose); +RcppExport SEXP _bssm_nonlinear_pm_mcmc(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP seedSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP sampling_methodSEXP, SEXP iekf_iterSEXP, SEXP output_typeSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); + Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(nonlinear_pm_mcmc(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type, verbose)); + return rcpp_result_gen; +END_RCPP +} +// nonlinear_da_mcmc +Rcpp::List nonlinear_da_mcmc(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const unsigned int seed, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int n_threads, const unsigned int max_iter, const double conv_tol, const unsigned int sampling_method, const unsigned int iekf_iter, const unsigned int output_type, const bool verbose); +RcppExport SEXP _bssm_nonlinear_da_mcmc(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP seedSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP sampling_methodSEXP, SEXP iekf_iterSEXP, SEXP output_typeSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); + Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(nonlinear_da_mcmc(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, max_iter, conv_tol, sampling_method, iekf_iter, output_type, verbose)); + return rcpp_result_gen; +END_RCPP +} +// nonlinear_ekf_mcmc +Rcpp::List nonlinear_ekf_mcmc(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const unsigned int seed, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int n_threads, const unsigned int iekf_iter, const unsigned int output_type, const bool verbose); +RcppExport SEXP _bssm_nonlinear_ekf_mcmc(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP seedSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP iekf_iterSEXP, SEXP output_typeSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(nonlinear_ekf_mcmc(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, iekf_iter, output_type, verbose)); + return rcpp_result_gen; +END_RCPP +} +// nonlinear_is_mcmc +Rcpp::List nonlinear_is_mcmc(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const unsigned int seed, const unsigned int nsim, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int n_threads, const unsigned int is_type, const unsigned int sampling_method, const unsigned int max_iter, const double conv_tol, const unsigned int iekf_iter, const unsigned int output_type, const bool approx, const bool verbose); +RcppExport SEXP _bssm_nonlinear_is_mcmc(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP seedSEXP, SEXP nsimSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP n_threadsSEXP, SEXP is_typeSEXP, SEXP sampling_methodSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP iekf_iterSEXP, SEXP output_typeSEXP, SEXP approxSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type sampling_method(sampling_methodSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); + Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const bool >::type approx(approxSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(nonlinear_is_mcmc(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, seed, nsim, iter, burnin, thin, gamma, target_acceptance, S, end_ram, n_threads, is_type, sampling_method, max_iter, conv_tol, iekf_iter, output_type, approx, verbose)); + return rcpp_result_gen; +END_RCPP +} +// R_milstein +double R_milstein(const double x0, const unsigned int L, const double t, const arma::vec& theta, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, bool positive, const unsigned int seed); +RcppExport SEXP _bssm_R_milstein(SEXP x0SEXP, SEXP LSEXP, SEXP tSEXP, SEXP thetaSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP positiveSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); + Rcpp::traits::input_parameter< const double >::type t(tSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); + Rcpp::traits::input_parameter< bool >::type positive(positiveSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(R_milstein(x0, L, t, theta, drift_pntr, diffusion_pntr, ddiffusion_pntr, positive, seed)); + return rcpp_result_gen; +END_RCPP +} +// suggest_n_nongaussian +arma::vec suggest_n_nongaussian(const Rcpp::List model_, const arma::vec theta, const arma::vec candidates, const unsigned int replications, const unsigned int seed, const int model_type); +RcppExport SEXP _bssm_suggest_n_nongaussian(SEXP model_SEXP, SEXP thetaSEXP, SEXP candidatesSEXP, SEXP replicationsSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const arma::vec >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const arma::vec >::type candidates(candidatesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type replications(replicationsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(suggest_n_nongaussian(model_, theta, candidates, replications, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// suggest_n_nonlinear +arma::vec suggest_n_nonlinear(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const arma::vec theta_map, const arma::vec candidates, const unsigned int replications, const unsigned int seed); +RcppExport SEXP _bssm_suggest_n_nonlinear(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP theta_mapSEXP, SEXP candidatesSEXP, SEXP replicationsSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const arma::vec >::type theta_map(theta_mapSEXP); + Rcpp::traits::input_parameter< const arma::vec >::type candidates(candidatesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type replications(replicationsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(suggest_n_nonlinear(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, theta_map, candidates, replications, seed)); + return rcpp_result_gen; +END_RCPP +} +// postcorrection_nongaussian +Rcpp::List postcorrection_nongaussian(const Rcpp::List model_, const int model_type, const unsigned int output_type, const unsigned int nsim, const unsigned int seed, const unsigned int n_threads, const unsigned int is_type, const arma::uvec counts, const arma::mat theta, const arma::cube modes); +RcppExport SEXP _bssm_postcorrection_nongaussian(SEXP model_SEXP, SEXP model_typeSEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP n_threadsSEXP, SEXP is_typeSEXP, SEXP countsSEXP, SEXP thetaSEXP, SEXP modesSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); + Rcpp::traits::input_parameter< const arma::uvec >::type counts(countsSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const arma::cube >::type modes(modesSEXP); + rcpp_result_gen = Rcpp::wrap(postcorrection_nongaussian(model_, model_type, output_type, nsim, seed, n_threads, is_type, counts, theta, modes)); + return rcpp_result_gen; +END_RCPP +} +// postcorrection_nonlinear +Rcpp::List postcorrection_nonlinear(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta_init, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int output_type, const unsigned int nsim, const unsigned int seed, const unsigned int n_threads, const unsigned int is_type, const arma::uvec counts, const arma::mat theta, const arma::cube modes); +RcppExport SEXP _bssm_postcorrection_nonlinear(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP theta_initSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP output_typeSEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP n_threadsSEXP, SEXP is_typeSEXP, SEXP countsSEXP, SEXP thetaSEXP, SEXP modesSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta_init(theta_initSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type output_type(output_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); + Rcpp::traits::input_parameter< const arma::uvec >::type counts(countsSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const arma::cube >::type modes(modesSEXP); + rcpp_result_gen = Rcpp::wrap(postcorrection_nonlinear(y, Z, H, T, R, Zg, Tg, a1, P1, theta_init, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, output_type, nsim, seed, n_threads, is_type, counts, theta, modes)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_predict +arma::cube gaussian_predict(const Rcpp::List model_, const arma::mat theta, const arma::mat alpha, const unsigned int predict_type, const unsigned int seed, const int model_type); +RcppExport SEXP _bssm_gaussian_predict(SEXP model_SEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const arma::mat >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type alpha(alphaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_predict(model_, theta, alpha, predict_type, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// nongaussian_predict +arma::cube nongaussian_predict(const Rcpp::List model_, const arma::mat& theta, const arma::mat& alpha, const unsigned int predict_type, const unsigned int seed, const unsigned int model_type); +RcppExport SEXP _bssm_nongaussian_predict(SEXP model_SEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type alpha(alphaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(nongaussian_predict(model_, theta, alpha, predict_type, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// nonlinear_predict +arma::cube nonlinear_predict(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const arma::mat& theta, const arma::mat& alpha, const unsigned int predict_type, const unsigned int seed); +RcppExport SEXP _bssm_nonlinear_predict(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type alpha(alphaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(nonlinear_predict(y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_predict_past +arma::cube gaussian_predict_past(const Rcpp::List model_, const arma::mat& theta, const arma::cube& alpha, const unsigned int predict_type, const unsigned int seed, const int model_type); +RcppExport SEXP _bssm_gaussian_predict_past(SEXP model_SEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const arma::cube& >::type alpha(alphaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_predict_past(model_, theta, alpha, predict_type, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// nongaussian_predict_past +arma::cube nongaussian_predict_past(const Rcpp::List model_, const arma::mat& theta, const arma::cube& alpha, const unsigned int predict_type, const unsigned int seed, const unsigned int model_type); +RcppExport SEXP _bssm_nongaussian_predict_past(SEXP model_SEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const arma::cube& >::type alpha(alphaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(nongaussian_predict_past(model_, theta, alpha, predict_type, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// nonlinear_predict_past +arma::cube nonlinear_predict_past(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const arma::uvec& time_varying, const unsigned int n_states, const unsigned int n_etas, const arma::mat& theta, const arma::cube& alpha, const unsigned int predict_type, const unsigned int seed); +RcppExport SEXP _bssm_nonlinear_predict_past(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP time_varyingSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP thetaSEXP, SEXP alphaSEXP, SEXP predict_typeSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const arma::cube& >::type alpha(alphaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type predict_type(predict_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(nonlinear_predict_past(y, Z, H, T, R, Zg, Tg, a1, P1, log_prior_pdf, known_params, known_tv_params, time_varying, n_states, n_etas, theta, alpha, predict_type, seed)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_psi_smoother +arma::cube gaussian_psi_smoother(const Rcpp::List model_, const unsigned int nsim, const unsigned int seed, const int model_type); +RcppExport SEXP _bssm_gaussian_psi_smoother(SEXP model_SEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_psi_smoother(model_, nsim, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// psi_smoother +Rcpp::List psi_smoother(const Rcpp::List model_, const unsigned int nsim, const unsigned int seed, const int model_type); +RcppExport SEXP _bssm_psi_smoother(SEXP model_SEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(psi_smoother(model_, nsim, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// psi_smoother_nlg +Rcpp::List psi_smoother_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const unsigned int nsim, const unsigned int seed, const unsigned int max_iter, const double conv_tol, const unsigned int iekf_iter); +RcppExport SEXP _bssm_psi_smoother_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP nsimSEXP, SEXP seedSEXP, SEXP max_iterSEXP, SEXP conv_tolSEXP, SEXP iekf_iterSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type max_iter(max_iterSEXP); + Rcpp::traits::input_parameter< const double >::type conv_tol(conv_tolSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iekf_iter(iekf_iterSEXP); + rcpp_result_gen = Rcpp::wrap(psi_smoother_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, nsim, seed, max_iter, conv_tol, iekf_iter)); + return rcpp_result_gen; +END_RCPP +} +// loglik_sde +double loglik_sde(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L, const unsigned int seed); +RcppExport SEXP _bssm_loglik_sde(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP LSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); + Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); + Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); + Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(loglik_sde(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed)); + return rcpp_result_gen; +END_RCPP +} +// bsf_sde +Rcpp::List bsf_sde(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L, const unsigned int seed); +RcppExport SEXP _bssm_bsf_sde(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP LSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); + Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); + Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); + Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(bsf_sde(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed)); + return rcpp_result_gen; +END_RCPP +} +// bsf_smoother_sde +Rcpp::List bsf_smoother_sde(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L, const unsigned int seed); +RcppExport SEXP _bssm_bsf_smoother_sde(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP LSEXP, SEXP seedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); + Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); + Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); + Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + rcpp_result_gen = Rcpp::wrap(bsf_smoother_sde(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed)); + return rcpp_result_gen; +END_RCPP +} +// sde_pm_mcmc +Rcpp::List sde_pm_mcmc(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L, const unsigned int seed, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int type, const bool verbose); +RcppExport SEXP _bssm_sde_pm_mcmc(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP LSEXP, SEXP seedSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP typeSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); + Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); + Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); + Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L(LSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type type(typeSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(sde_pm_mcmc(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type, verbose)); + return rcpp_result_gen; +END_RCPP +} +// sde_da_mcmc +Rcpp::List sde_da_mcmc(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L_c, const unsigned int L_f, const unsigned int seed, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int type, const bool verbose); +RcppExport SEXP _bssm_sde_da_mcmc(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP L_cSEXP, SEXP L_fSEXP, SEXP seedSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP typeSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); + Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); + Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); + Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L_c(L_cSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L_f(L_fSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type type(typeSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(sde_da_mcmc(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, type, verbose)); + return rcpp_result_gen; +END_RCPP +} +// sde_is_mcmc +Rcpp::List sde_is_mcmc(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const arma::vec& theta, const unsigned int nsim, const unsigned int L_c, const unsigned int L_f, const unsigned int seed, const unsigned int iter, const unsigned int burnin, const unsigned int thin, const double gamma, const double target_acceptance, const arma::mat S, const bool end_ram, const unsigned int is_type, const unsigned int n_threads, const unsigned int type, const bool verbose); +RcppExport SEXP _bssm_sde_is_mcmc(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP thetaSEXP, SEXP nsimSEXP, SEXP L_cSEXP, SEXP L_fSEXP, SEXP seedSEXP, SEXP iterSEXP, SEXP burninSEXP, SEXP thinSEXP, SEXP gammaSEXP, SEXP target_acceptanceSEXP, SEXP SSEXP, SEXP end_ramSEXP, SEXP is_typeSEXP, SEXP n_threadsSEXP, SEXP typeSEXP, SEXP verboseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); + Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); + Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); + Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L_c(L_cSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L_f(L_fSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type iter(iterSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type burnin(burninSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type thin(thinSEXP); + Rcpp::traits::input_parameter< const double >::type gamma(gammaSEXP); + Rcpp::traits::input_parameter< const double >::type target_acceptance(target_acceptanceSEXP); + Rcpp::traits::input_parameter< const arma::mat >::type S(SSEXP); + Rcpp::traits::input_parameter< const bool >::type end_ram(end_ramSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type is_type(is_typeSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_threads(n_threadsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type type(typeSEXP); + Rcpp::traits::input_parameter< const bool >::type verbose(verboseSEXP); + rcpp_result_gen = Rcpp::wrap(sde_is_mcmc(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, theta, nsim, L_c, L_f, seed, iter, burnin, thin, gamma, target_acceptance, S, end_ram, is_type, n_threads, type, verbose)); + return rcpp_result_gen; +END_RCPP +} +// sde_state_sampler_bsf_is2 +Rcpp::List sde_state_sampler_bsf_is2(const arma::vec& y, const double x0, const bool positive, SEXP drift_pntr, SEXP diffusion_pntr, SEXP ddiffusion_pntr, SEXP log_prior_pdf_pntr, SEXP log_obs_density_pntr, const unsigned int nsim, const unsigned int L_f, const unsigned int seed, const arma::vec& approx_loglik_storage, const arma::mat& theta); +RcppExport SEXP _bssm_sde_state_sampler_bsf_is2(SEXP ySEXP, SEXP x0SEXP, SEXP positiveSEXP, SEXP drift_pntrSEXP, SEXP diffusion_pntrSEXP, SEXP ddiffusion_pntrSEXP, SEXP log_prior_pdf_pntrSEXP, SEXP log_obs_density_pntrSEXP, SEXP nsimSEXP, SEXP L_fSEXP, SEXP seedSEXP, SEXP approx_loglik_storageSEXP, SEXP thetaSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec& >::type y(ySEXP); + Rcpp::traits::input_parameter< const double >::type x0(x0SEXP); + Rcpp::traits::input_parameter< const bool >::type positive(positiveSEXP); + Rcpp::traits::input_parameter< SEXP >::type drift_pntr(drift_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type diffusion_pntr(diffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type ddiffusion_pntr(ddiffusion_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf_pntr(log_prior_pdf_pntrSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_obs_density_pntr(log_obs_density_pntrSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type L_f(L_fSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type approx_loglik_storage(approx_loglik_storageSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type theta(thetaSEXP); + rcpp_result_gen = Rcpp::wrap(sde_state_sampler_bsf_is2(y, x0, positive, drift_pntr, diffusion_pntr, ddiffusion_pntr, log_prior_pdf_pntr, log_obs_density_pntr, nsim, L_f, seed, approx_loglik_storage, theta)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_smoother +Rcpp::List gaussian_smoother(const Rcpp::List model_, const int model_type); +RcppExport SEXP _bssm_gaussian_smoother(SEXP model_SEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_smoother(model_, model_type)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_ccov_smoother +Rcpp::List gaussian_ccov_smoother(const Rcpp::List model_, const int model_type); +RcppExport SEXP _bssm_gaussian_ccov_smoother(SEXP model_SEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_ccov_smoother(model_, model_type)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_fast_smoother +arma::mat gaussian_fast_smoother(const Rcpp::List model_, const int model_type); +RcppExport SEXP _bssm_gaussian_fast_smoother(SEXP model_SEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_fast_smoother(model_, model_type)); + return rcpp_result_gen; +END_RCPP +} +// gaussian_sim_smoother +arma::cube gaussian_sim_smoother(const Rcpp::List model_, const unsigned int nsim, bool use_antithetic, const unsigned int seed, const int model_type); +RcppExport SEXP _bssm_gaussian_sim_smoother(SEXP model_SEXP, SEXP nsimSEXP, SEXP use_antitheticSEXP, SEXP seedSEXP, SEXP model_typeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const Rcpp::List >::type model_(model_SEXP); + Rcpp::traits::input_parameter< const unsigned int >::type nsim(nsimSEXP); + Rcpp::traits::input_parameter< bool >::type use_antithetic(use_antitheticSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type seed(seedSEXP); + Rcpp::traits::input_parameter< const int >::type model_type(model_typeSEXP); + rcpp_result_gen = Rcpp::wrap(gaussian_sim_smoother(model_, nsim, use_antithetic, seed, model_type)); + return rcpp_result_gen; +END_RCPP +} +// ukf_nlg +Rcpp::List ukf_nlg(const arma::mat& y, SEXP Z, SEXP H, SEXP T, SEXP R, SEXP Zg, SEXP Tg, SEXP a1, SEXP P1, const arma::vec& theta, SEXP log_prior_pdf, const arma::vec& known_params, const arma::mat& known_tv_params, const unsigned int n_states, const unsigned int n_etas, const arma::uvec& time_varying, const double alpha, const double beta, const double kappa); +RcppExport SEXP _bssm_ukf_nlg(SEXP ySEXP, SEXP ZSEXP, SEXP HSEXP, SEXP TSEXP, SEXP RSEXP, SEXP ZgSEXP, SEXP TgSEXP, SEXP a1SEXP, SEXP P1SEXP, SEXP thetaSEXP, SEXP log_prior_pdfSEXP, SEXP known_paramsSEXP, SEXP known_tv_paramsSEXP, SEXP n_statesSEXP, SEXP n_etasSEXP, SEXP time_varyingSEXP, SEXP alphaSEXP, SEXP betaSEXP, SEXP kappaSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type y(ySEXP); + Rcpp::traits::input_parameter< SEXP >::type Z(ZSEXP); + Rcpp::traits::input_parameter< SEXP >::type H(HSEXP); + Rcpp::traits::input_parameter< SEXP >::type T(TSEXP); + Rcpp::traits::input_parameter< SEXP >::type R(RSEXP); + Rcpp::traits::input_parameter< SEXP >::type Zg(ZgSEXP); + Rcpp::traits::input_parameter< SEXP >::type Tg(TgSEXP); + Rcpp::traits::input_parameter< SEXP >::type a1(a1SEXP); + Rcpp::traits::input_parameter< SEXP >::type P1(P1SEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type theta(thetaSEXP); + Rcpp::traits::input_parameter< SEXP >::type log_prior_pdf(log_prior_pdfSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type known_params(known_paramsSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type known_tv_params(known_tv_paramsSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_states(n_statesSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type n_etas(n_etasSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type time_varying(time_varyingSEXP); + Rcpp::traits::input_parameter< const double >::type alpha(alphaSEXP); + Rcpp::traits::input_parameter< const double >::type beta(betaSEXP); + Rcpp::traits::input_parameter< const double >::type kappa(kappaSEXP); + rcpp_result_gen = Rcpp::wrap(ukf_nlg(y, Z, H, T, R, Zg, Tg, a1, P1, theta, log_prior_pdf, known_params, known_tv_params, n_states, n_etas, time_varying, alpha, beta, kappa)); + return rcpp_result_gen; +END_RCPP +} +// conditional_cov +void conditional_cov(arma::cube& Vt, arma::cube& Ct, const bool use_svd); +RcppExport SEXP _bssm_conditional_cov(SEXP VtSEXP, SEXP CtSEXP, SEXP use_svdSEXP) { +BEGIN_RCPP + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< arma::cube& >::type Vt(VtSEXP); + Rcpp::traits::input_parameter< arma::cube& >::type Ct(CtSEXP); + Rcpp::traits::input_parameter< const bool >::type use_svd(use_svdSEXP); + conditional_cov(Vt, Ct, use_svd); + return R_NilValue; +END_RCPP +} +// dmvnorm +double dmvnorm(const arma::vec& x, const arma::vec& mean, const arma::mat& sigma, bool lwr, bool logd); +RcppExport SEXP _bssm_dmvnorm(SEXP xSEXP, SEXP meanSEXP, SEXP sigmaSEXP, SEXP lwrSEXP, SEXP logdSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec& >::type x(xSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type mean(meanSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type sigma(sigmaSEXP); + Rcpp::traits::input_parameter< bool >::type lwr(lwrSEXP); + Rcpp::traits::input_parameter< bool >::type logd(logdSEXP); + rcpp_result_gen = Rcpp::wrap(dmvnorm(x, mean, sigma, lwr, logd)); + return rcpp_result_gen; +END_RCPP +} +// precompute_dmvnorm +double precompute_dmvnorm(const arma::mat& sigma, arma::mat& Linv, const arma::uvec& nonzero); +RcppExport SEXP _bssm_precompute_dmvnorm(SEXP sigmaSEXP, SEXP LinvSEXP, SEXP nonzeroSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type sigma(sigmaSEXP); + Rcpp::traits::input_parameter< arma::mat& >::type Linv(LinvSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type nonzero(nonzeroSEXP); + rcpp_result_gen = Rcpp::wrap(precompute_dmvnorm(sigma, Linv, nonzero)); + return rcpp_result_gen; +END_RCPP +} +// fast_dmvnorm +double fast_dmvnorm(const arma::vec& x, const arma::vec& mean, const arma::mat& Linv, const arma::uvec& nonzero, const double constant); +RcppExport SEXP _bssm_fast_dmvnorm(SEXP xSEXP, SEXP meanSEXP, SEXP LinvSEXP, SEXP nonzeroSEXP, SEXP constantSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::vec& >::type x(xSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type mean(meanSEXP); + Rcpp::traits::input_parameter< const arma::mat& >::type Linv(LinvSEXP); + Rcpp::traits::input_parameter< const arma::uvec& >::type nonzero(nonzeroSEXP); + Rcpp::traits::input_parameter< const double >::type constant(constantSEXP); + rcpp_result_gen = Rcpp::wrap(fast_dmvnorm(x, mean, Linv, nonzero, constant)); + return rcpp_result_gen; +END_RCPP +} +// psd_chol +arma::mat psd_chol(const arma::mat& x); +RcppExport SEXP _bssm_psd_chol(SEXP xSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const arma::mat& >::type x(xSEXP); + rcpp_result_gen = Rcpp::wrap(psd_chol(x)); + return rcpp_result_gen; +END_RCPP +} +// stratified_sample +arma::uvec stratified_sample(arma::vec& p, const arma::vec& r, const unsigned int N); +RcppExport SEXP _bssm_stratified_sample(SEXP pSEXP, SEXP rSEXP, SEXP NSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< arma::vec& >::type p(pSEXP); + Rcpp::traits::input_parameter< const arma::vec& >::type r(rSEXP); + Rcpp::traits::input_parameter< const unsigned int >::type N(NSEXP); + rcpp_result_gen = Rcpp::wrap(stratified_sample(p, r, N)); + return rcpp_result_gen; +END_RCPP +} + +static const R_CallMethodDef CallEntries[] = { + {"_bssm_gaussian_approx_model", (DL_FUNC) &_bssm_gaussian_approx_model, 2}, + {"_bssm_gaussian_approx_model_nlg", (DL_FUNC) &_bssm_gaussian_approx_model_nlg, 19}, + {"_bssm_bsf", (DL_FUNC) &_bssm_bsf, 5}, + {"_bssm_bsf_smoother", (DL_FUNC) &_bssm_bsf_smoother, 5}, + {"_bssm_bsf_nlg", (DL_FUNC) &_bssm_bsf_nlg, 18}, + {"_bssm_bsf_smoother_nlg", (DL_FUNC) &_bssm_bsf_smoother_nlg, 18}, + {"_bssm_ekf_nlg", (DL_FUNC) &_bssm_ekf_nlg, 17}, + {"_bssm_ekf_smoother_nlg", (DL_FUNC) &_bssm_ekf_smoother_nlg, 17}, + {"_bssm_ekf_fast_smoother_nlg", (DL_FUNC) &_bssm_ekf_fast_smoother_nlg, 17}, + {"_bssm_ekpf", (DL_FUNC) &_bssm_ekpf, 18}, + {"_bssm_ekpf_smoother", (DL_FUNC) &_bssm_ekpf_smoother, 18}, + {"_bssm_IACT", (DL_FUNC) &_bssm_IACT, 1}, + {"_bssm_importance_sample_ng", (DL_FUNC) &_bssm_importance_sample_ng, 5}, + {"_bssm_gaussian_kfilter", (DL_FUNC) &_bssm_gaussian_kfilter, 2}, + {"_bssm_gaussian_loglik", (DL_FUNC) &_bssm_gaussian_loglik, 2}, + {"_bssm_nongaussian_loglik", (DL_FUNC) &_bssm_nongaussian_loglik, 5}, + {"_bssm_nonlinear_loglik", (DL_FUNC) &_bssm_nonlinear_loglik, 22}, + {"_bssm_gaussian_mcmc", (DL_FUNC) &_bssm_gaussian_mcmc, 13}, + {"_bssm_nongaussian_pm_mcmc", (DL_FUNC) &_bssm_nongaussian_pm_mcmc, 15}, + {"_bssm_nongaussian_da_mcmc", (DL_FUNC) &_bssm_nongaussian_da_mcmc, 15}, + {"_bssm_nongaussian_is_mcmc", (DL_FUNC) &_bssm_nongaussian_is_mcmc, 17}, + {"_bssm_nonlinear_pm_mcmc", (DL_FUNC) &_bssm_nonlinear_pm_mcmc, 32}, + {"_bssm_nonlinear_da_mcmc", (DL_FUNC) &_bssm_nonlinear_da_mcmc, 32}, + {"_bssm_nonlinear_ekf_mcmc", (DL_FUNC) &_bssm_nonlinear_ekf_mcmc, 28}, + {"_bssm_nonlinear_is_mcmc", (DL_FUNC) &_bssm_nonlinear_is_mcmc, 34}, + {"_bssm_R_milstein", (DL_FUNC) &_bssm_R_milstein, 9}, + {"_bssm_suggest_n_nongaussian", (DL_FUNC) &_bssm_suggest_n_nongaussian, 6}, + {"_bssm_suggest_n_nonlinear", (DL_FUNC) &_bssm_suggest_n_nonlinear, 20}, + {"_bssm_postcorrection_nongaussian", (DL_FUNC) &_bssm_postcorrection_nongaussian, 10}, + {"_bssm_postcorrection_nonlinear", (DL_FUNC) &_bssm_postcorrection_nonlinear, 24}, + {"_bssm_gaussian_predict", (DL_FUNC) &_bssm_gaussian_predict, 6}, + {"_bssm_nongaussian_predict", (DL_FUNC) &_bssm_nongaussian_predict, 6}, + {"_bssm_nonlinear_predict", (DL_FUNC) &_bssm_nonlinear_predict, 19}, + {"_bssm_gaussian_predict_past", (DL_FUNC) &_bssm_gaussian_predict_past, 6}, + {"_bssm_nongaussian_predict_past", (DL_FUNC) &_bssm_nongaussian_predict_past, 6}, + {"_bssm_nonlinear_predict_past", (DL_FUNC) &_bssm_nonlinear_predict_past, 19}, + {"_bssm_gaussian_psi_smoother", (DL_FUNC) &_bssm_gaussian_psi_smoother, 4}, + {"_bssm_psi_smoother", (DL_FUNC) &_bssm_psi_smoother, 4}, + {"_bssm_psi_smoother_nlg", (DL_FUNC) &_bssm_psi_smoother_nlg, 21}, + {"_bssm_loglik_sde", (DL_FUNC) &_bssm_loglik_sde, 12}, + {"_bssm_bsf_sde", (DL_FUNC) &_bssm_bsf_sde, 12}, + {"_bssm_bsf_smoother_sde", (DL_FUNC) &_bssm_bsf_smoother_sde, 12}, + {"_bssm_sde_pm_mcmc", (DL_FUNC) &_bssm_sde_pm_mcmc, 21}, + {"_bssm_sde_da_mcmc", (DL_FUNC) &_bssm_sde_da_mcmc, 22}, + {"_bssm_sde_is_mcmc", (DL_FUNC) &_bssm_sde_is_mcmc, 24}, + {"_bssm_sde_state_sampler_bsf_is2", (DL_FUNC) &_bssm_sde_state_sampler_bsf_is2, 13}, + {"_bssm_gaussian_smoother", (DL_FUNC) &_bssm_gaussian_smoother, 2}, + {"_bssm_gaussian_ccov_smoother", (DL_FUNC) &_bssm_gaussian_ccov_smoother, 2}, + {"_bssm_gaussian_fast_smoother", (DL_FUNC) &_bssm_gaussian_fast_smoother, 2}, + {"_bssm_gaussian_sim_smoother", (DL_FUNC) &_bssm_gaussian_sim_smoother, 5}, + {"_bssm_ukf_nlg", (DL_FUNC) &_bssm_ukf_nlg, 19}, + {"_bssm_conditional_cov", (DL_FUNC) &_bssm_conditional_cov, 3}, + {"_bssm_dmvnorm", (DL_FUNC) &_bssm_dmvnorm, 5}, + {"_bssm_precompute_dmvnorm", (DL_FUNC) &_bssm_precompute_dmvnorm, 3}, + {"_bssm_fast_dmvnorm", (DL_FUNC) &_bssm_fast_dmvnorm, 5}, + {"_bssm_psd_chol", (DL_FUNC) &_bssm_psd_chol, 1}, + {"_bssm_stratified_sample", (DL_FUNC) &_bssm_stratified_sample, 3}, + {NULL, NULL, 0} +}; + +RcppExport void R_init_bssm(DllInfo *dll) { + R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); + R_useDynamicSymbols(dll, FALSE); +} diff --git a/src/approx_mcmc.cpp b/src/approx_mcmc.cpp index a0240677..91e573f1 100644 --- a/src/approx_mcmc.cpp +++ b/src/approx_mcmc.cpp @@ -22,9 +22,9 @@ approx_mcmc::approx_mcmc(const unsigned int iter, const unsigned int burnin, const unsigned int thin, const unsigned int n, const unsigned int m, const unsigned int k, const double target_acceptance, const double gamma, const arma::mat& S, const unsigned int output_type, - const bool store_modes) : + const bool store_modes, const bool verbose) : mcmc(iter, burnin, thin, n, m, - target_acceptance, gamma, S, output_type), + target_acceptance, gamma, S, output_type, verbose), weight_storage(arma::vec(n_samples, arma::fill::zeros)), mode_storage(arma::cube(k, n, n_samples * store_modes)), approx_loglik_storage(arma::vec(n_samples)), @@ -117,7 +117,7 @@ void approx_mcmc::amcmc(T model, const unsigned int method, const bool end_ram, // compute the log[p(theta)] double logprior = model.log_prior_pdf(theta, prior_fn); - if (!arma::is_finite(logprior)) { + if (!std::isfinite(logprior)) { Rcpp::stop("Initial prior probability is not finite."); } // placeholders @@ -141,12 +141,17 @@ void approx_mcmc::amcmc(T model, const unsigned int method, const bool end_ram, unsigned int n_values = 0; double acceptance_prob = 0.0; + // don't update progress at each iteration + unsigned int mod = std::max(1U, iter / 50); + unsigned int ticks = 1; + if (verbose) { + Rcpp::Rcout<<"Starting MCMC. Progress:\n"; + Rcpp::Rcout<<"0% 10 20 30 40 50 60 70 80 90 100%\n"; + Rcpp::Rcout<<"|"; + } + for (unsigned int i = 1; i <= iter; i++) { - if (i % 16 == 0) { - Rcpp::checkUserInterrupt(); - } - // sample from standard normal distribution arma::vec u(n_par); for(unsigned int j = 0; j < n_par; j++) { @@ -196,11 +201,23 @@ void approx_mcmc::amcmc(T model, const unsigned int method, const bool end_ram, } } - if (!end_ram || i <= burnin) { ramcmc::adapt_S(S, u, acceptance_prob, target_acceptance, i, gamma); } + if (i % mod == 0) { + Rcpp::checkUserInterrupt(); + if (verbose) { + if (ticks % 5 == 0) { + Rcpp::Rcout<<"|"; + } else { + Rcpp::Rcout<<"-"; + } + ticks++; + } + } } + if (verbose) Rcpp::Rcout<<"\n"; + if (n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC. Check your model."); trim_storage(); acceptance_rate /= (iter - burnin); @@ -216,7 +233,7 @@ void approx_mcmc::amcmc(ssm_sde model, const unsigned int nsim, const bool end_r unsigned n = model.n; // compute the log[p(theta)] double logprior = model.log_prior_pdf(model.theta); - if (!arma::is_finite(logprior)) { + if (!std::isfinite(logprior)) { Rcpp::stop("Initial prior probability is not finite."); } @@ -234,10 +251,16 @@ void approx_mcmc::amcmc(ssm_sde model, const unsigned int nsim, const bool end_r std::uniform_real_distribution<> unif(0.0, 1.0); arma::vec theta = model.theta; + // don't update progress at each iteration + unsigned int mod = std::max(1U, iter / 50); + unsigned int ticks = 1; + if (verbose) { + Rcpp::Rcout<<"Starting MCMC. Progress:\n"; + Rcpp::Rcout<<"0% 10 20 30 40 50 60 70 80 90 100%\n"; + Rcpp::Rcout<<"|"; + } + for (unsigned int i = 1; i <= iter; i++) { - if (i % 4 == 0) { - Rcpp::checkUserInterrupt(); - } // sample from standard normal distribution arma::vec u(n_par); @@ -250,7 +273,7 @@ void approx_mcmc::amcmc(ssm_sde model, const unsigned int nsim, const bool end_r // compute prior double logprior_prop = model.log_prior_pdf(theta_prop); - if (arma::is_finite(logprior_prop)) { + if (std::isfinite(logprior_prop)) { // update parameters model.theta = theta_prop; @@ -291,7 +314,20 @@ void approx_mcmc::amcmc(ssm_sde model, const unsigned int nsim, const bool end_r if (!end_ram || i <= burnin) { ramcmc::adapt_S(S, u, acceptance_prob, target_acceptance, i, gamma); } + if (i % mod == 0) { + Rcpp::checkUserInterrupt(); + if (verbose) { + if (ticks % 5 == 0) { + Rcpp::Rcout<<"|"; + } else { + Rcpp::Rcout<<"-"; + } + ticks++; + } + } } + if (verbose) Rcpp::Rcout<<"\n"; + if (n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC. Check your model."); trim_storage(); acceptance_rate /= (iter - burnin); @@ -318,6 +354,9 @@ template void approx_mcmc::is_correction_psi(T model, const unsigned int nsim, const unsigned int is_type, const unsigned int n_threads, const Rcpp::Function update_fn) { + if (verbose) { + Rcpp::Rcout<<"\nStarting IS-correction phase with "< void mcmc::state_posterior(ssm_mlg model, const unsigned int n_threads, const Rcpp::Function update_fn) { - - + + #ifdef _OPENMP parset_mlg pars(model, theta_storage, update_fn); #pragma omp parallel num_threads(n_threads) default(shared) firstprivate(model) @@ -210,12 +211,18 @@ void mcmc::mcmc_gaussian(T model, const bool end_ram, bool new_value = true; unsigned int n_values = 0; + + // don't update progress at each iteration + unsigned int mod = std::max(1U, iter / 50); + unsigned int ticks = 1; + if (verbose) { + Rcpp::Rcout<<"Starting MCMC. Progress:\n"; + Rcpp::Rcout<<"0% 10 20 30 40 50 60 70 80 90 100%\n"; + Rcpp::Rcout<<"|"; + } + for (unsigned int i = 1; i <= iter; i++) { - if (i % 16 == 0) { - Rcpp::checkUserInterrupt(); - } - // sample from standard normal distribution arma::vec u(n_par); for(unsigned int j = 0; j < n_par; j++) { @@ -269,9 +276,21 @@ void mcmc::mcmc_gaussian(T model, const bool end_ram, if (!end_ram || i <= burnin) { ramcmc::adapt_S(S, u, acceptance_prob, target_acceptance, i, gamma); } - + if (i % mod == 0) { + Rcpp::checkUserInterrupt(); + if (verbose) { + if (ticks % 5 == 0) { + Rcpp::Rcout<<"|"; + } else { + Rcpp::Rcout<<"-"; + } + ticks++; + } + } } - if(n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC run. Check your model."); + if (verbose) Rcpp::Rcout<<"\n"; + if(n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC. Check your model."); + trim_storage(); acceptance_rate /= (iter - burnin); @@ -331,7 +350,7 @@ void mcmc::pm_mcmc( model.update_model(theta, update_fn); // just in case // compute the log[p(theta)] double logprior = model.log_prior_pdf(theta, prior_fn); - if (!arma::is_finite(logprior)) { + if (!std::isfinite(logprior)) { Rcpp::stop("Initial prior probability is not finite."); } arma::cube alpha(m, n + 1, nsim); @@ -360,12 +379,18 @@ void mcmc::pm_mcmc( unsigned int n_values = 0; std::normal_distribution<> normal(0.0, 1.0); std::uniform_real_distribution<> unif(0.0, 1.0); + + // don't update progress at each iteration + unsigned int mod = std::max(1U, iter / 50); + unsigned int ticks = 1; + if (verbose) { + Rcpp::Rcout<<"Starting MCMC. Progress:\n"; + Rcpp::Rcout<<"0% 10 20 30 40 50 60 70 80 90 100%\n"; + Rcpp::Rcout<<"|"; + } + for (unsigned int i = 1; i <= iter; i++) { - if (i % 16 == 0) { - Rcpp::checkUserInterrupt(); - } - // sample from standard normal distribution arma::vec u(n_par); for(unsigned int j = 0; j < n_par; j++) { @@ -442,12 +467,25 @@ void mcmc::pm_mcmc( if (!end_ram || i <= burnin) { ramcmc::adapt_S(S, u, acceptance_prob, target_acceptance, i, gamma); } + if (i % mod == 0) { + Rcpp::checkUserInterrupt(); + if (verbose) { + if (ticks % 5 == 0) { + Rcpp::Rcout<<"|"; + } else { + Rcpp::Rcout<<"-"; + } + ticks++; + } + } } + if (verbose) Rcpp::Rcout<<"\n"; + if(n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC. Check your model."); + if (output_type == 2) { Vt += Valphahat / (iter - burnin); // Var[E(alpha)] + E[Var(alpha)] } - if(n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC run. Check your model."); trim_storage(); acceptance_rate /= (iter - burnin); } @@ -504,7 +542,7 @@ void mcmc::da_mcmc(T model, model.update_model(theta, update_fn); // just in case // compute the log[p(theta)] double logprior = model.log_prior_pdf(theta, prior_fn); - if (!arma::is_finite(logprior)) { + if (!std::isfinite(logprior)) { Rcpp::stop("Initial prior probability is not finite."); } arma::cube alpha(m, n + 1, nsim); @@ -534,12 +572,18 @@ void mcmc::da_mcmc(T model, unsigned int n_values = 0; std::normal_distribution<> normal(0.0, 1.0); std::uniform_real_distribution<> unif(0.0, 1.0); + + // don't update progress at each iteration + unsigned int mod = std::max(1U, iter / 50); + unsigned int ticks = 1; + if (verbose) { + Rcpp::Rcout<<"Starting MCMC. Progress:\n"; + Rcpp::Rcout<<"0% 10 20 30 40 50 60 70 80 90 100%\n"; + Rcpp::Rcout<<"|"; + } + for (unsigned int i = 1; i <= iter; i++) { - if (i % 16 == 0) { - Rcpp::checkUserInterrupt(); - } - // sample from standard normal distribution arma::vec u(n_par); for(unsigned int j = 0; j < n_par; j++) { @@ -618,7 +662,21 @@ void mcmc::da_mcmc(T model, if (!end_ram || i <= burnin) { ramcmc::adapt_S(S, u, acceptance_prob, target_acceptance, i, gamma); } + if (i % mod == 0) { + Rcpp::checkUserInterrupt(); + if (verbose) { + if (ticks % 5 == 0) { + Rcpp::Rcout<<"|"; + } else { + Rcpp::Rcout<<"-"; + } + ticks++; + } + } } + if (verbose) Rcpp::Rcout<<"\n"; + if(n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC. Check your model."); + if (output_type == 2) { Vt += Valphahat / (iter - burnin); // Var[E(alpha)] + E[Var(alpha)] } @@ -640,7 +698,7 @@ void mcmc::pm_mcmc( model.update_model(theta); // just in case // compute the log[p(theta)] double logprior = model.log_prior_pdf(theta); - if (!arma::is_finite(logprior)) { + if (!std::isfinite(logprior)) { Rcpp::stop("Initial prior probability is not finite."); } arma::cube alpha(m, n + 1, nsim); @@ -669,12 +727,18 @@ void mcmc::pm_mcmc( unsigned int n_values = 0; std::normal_distribution<> normal(0.0, 1.0); std::uniform_real_distribution<> unif(0.0, 1.0); + + // don't update progress at each iteration + unsigned int mod = std::max(1U, iter / 50); + unsigned int ticks = 1; + if (verbose) { + Rcpp::Rcout<<"Starting MCMC. Progress:\n"; + Rcpp::Rcout<<"0% 10 20 30 40 50 60 70 80 90 100%\n"; + Rcpp::Rcout<<"|"; + } + for (unsigned int i = 1; i <= iter; i++) { - if (i % 16 == 0) { - Rcpp::checkUserInterrupt(); - } - // sample from standard normal distribution arma::vec u(n_par); for(unsigned int j = 0; j < n_par; j++) { @@ -749,12 +813,25 @@ void mcmc::pm_mcmc( if (!end_ram || i <= burnin) { ramcmc::adapt_S(S, u, acceptance_prob, target_acceptance, i, gamma); } + if (i % mod == 0) { + Rcpp::checkUserInterrupt(); + if (verbose) { + if (ticks % 5 == 0) { + Rcpp::Rcout<<"|"; + } else { + Rcpp::Rcout<<"-"; + } + ticks++; + } + } } + if (verbose) Rcpp::Rcout<<"\n"; + if(n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC. Check your model."); + if (output_type == 2) { Vt += Valphahat / (iter - burnin); // Var[E(alpha)] + E[Var(alpha)] } - if(n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC run. Check your model."); trim_storage(); acceptance_rate /= (iter - burnin); } @@ -768,7 +845,7 @@ void mcmc::da_mcmc(ssm_sde model, model.update_model(theta); // just in case // compute the log[p(theta)] double logprior = model.log_prior_pdf(theta); - if (!arma::is_finite(logprior)) { + if (!std::isfinite(logprior)) { Rcpp::stop("Initial prior probability is not finite."); } @@ -781,7 +858,7 @@ void mcmc::da_mcmc(ssm_sde model, double ll_c = model.bsf_filter(nsim, model.L_c, alpha, weights, indices); double ll_f = model.bsf_filter(nsim, model.L_f, alpha, weights, indices); - + if (!std::isfinite(ll_f)) Rcpp::stop("Initial log-likelihood is not finite."); @@ -801,12 +878,18 @@ void mcmc::da_mcmc(ssm_sde model, unsigned int n_values = 0; std::normal_distribution<> normal(0.0, 1.0); std::uniform_real_distribution<> unif(0.0, 1.0); + + // don't update progress at each iteration + unsigned int mod = std::max(1U, iter / 50); + unsigned int ticks = 1; + if (verbose) { + Rcpp::Rcout<<"Starting MCMC. Progress:\n"; + Rcpp::Rcout<<"0% 10 20 30 40 50 60 70 80 90 100%\n"; + Rcpp::Rcout<<"|"; + } + for (unsigned int i = 1; i <= iter; i++) { - if (i % 16 == 0) { - Rcpp::checkUserInterrupt(); - } - // sample from standard normal distribution arma::vec u(n_par); for(unsigned int j = 0; j < n_par; j++) { @@ -884,13 +967,26 @@ void mcmc::da_mcmc(ssm_sde model, if (!end_ram || i <= burnin) { ramcmc::adapt_S(S, u, acceptance_prob, target_acceptance, i, gamma); + } + if (i % mod == 0) { + Rcpp::checkUserInterrupt(); + if (verbose) { + if (ticks % 5 == 0) { + Rcpp::Rcout<<"|"; + } else { + Rcpp::Rcout<<"-"; + } + ticks++; + } } } + if (verbose) Rcpp::Rcout<<"\n"; + if(n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC. Check your model."); + if (output_type == 2) { Vt += Valphahat / (iter - burnin); // Var[E(alpha)] + E[Var(alpha)] } - if(n_stored == 0) Rcpp::stop("No proposals were accepted in MCMC run. Check your model."); trim_storage(); acceptance_rate /= (iter - burnin); } diff --git a/src/mcmc.h b/src/mcmc.h index bc43e53f..e2878d27 100644 --- a/src/mcmc.h +++ b/src/mcmc.h @@ -31,7 +31,7 @@ class mcmc { mcmc(const unsigned int iter, const unsigned int burnin, const unsigned int thin, const unsigned int n, const unsigned int m, const double target_acceptance, const double gamma, const arma::mat& S, - const unsigned int output_type = 1); + const unsigned int output_type = 1, const bool verbose = true); // sample states given theta template @@ -42,7 +42,7 @@ class mcmc { void state_summary(T model, const Rcpp::Function update_fn = default_update_fn); - // gaussian mcmc + // linear-gaussian mcmc template void mcmc_gaussian(T model, const bool end_ram, const Rcpp::Function update_fn = default_update_fn, @@ -75,6 +75,7 @@ class mcmc { arma::mat S; double acceptance_rate; unsigned int output_type; + bool verbose; }; diff --git a/src/model_ar1_lg.cpp b/src/model_ar1_lg.cpp index 32f79d8c..ab34113c 100644 --- a/src/model_ar1_lg.cpp +++ b/src/model_ar1_lg.cpp @@ -11,20 +11,20 @@ ar1_lg::ar1_lg(const Rcpp::List model, const unsigned int seed) : void ar1_lg::update_model(const arma::vec& new_theta) { - - T(0, 0, 0) = new_theta(0); - R(0, 0, 0) = new_theta(1); + double rho = new_theta(0); + double sigma = std::exp(new_theta(1)); + T(0, 0, 0) = rho; + R(0, 0, 0) = sigma; + RR(0, 0, 0) = std::pow(sigma, 2); if (mu_est) { a1(0) = new_theta(2); - C.fill(new_theta(2) * (1.0 - new_theta(0))); + C.fill(new_theta(2) * (1.0 - rho)); } - P1(0, 0) = std::pow(new_theta(1), 2) / (1.0 - std::pow(new_theta(0), 2)); - - compute_RR(); + P1(0, 0) = RR(0, 0, 0) / (1.0 - std::pow(rho, 2)); if(sd_y_est) { - H(0) = new_theta(2 + mu_est); - HH(0) = H(0); + H(0) = std::exp(new_theta(2 + mu_est)); + HH(0) = std::pow(H(0), 2); } if(xreg.n_cols > 0) { @@ -35,20 +35,20 @@ void ar1_lg::update_model(const arma::vec& new_theta) { } void ar1_lg::update_model(const arma::vec& new_theta, const Rcpp::Function update_fn) { - - T(0, 0, 0) = new_theta(0); - R(0, 0, 0) = new_theta(1); + double rho = new_theta(0); + double sigma = std::exp(new_theta(1)); + T(0, 0, 0) = rho; + R(0, 0, 0) = sigma; + RR(0, 0, 0) = std::pow(sigma, 2); if (mu_est) { a1(0) = new_theta(2); - C.fill(new_theta(2) * (1.0 - new_theta(0))); + C.fill(new_theta(2) * (1.0 - rho)); } - P1(0, 0) = std::pow(new_theta(1), 2) / (1.0 - std::pow(new_theta(0), 2)); - - compute_RR(); + P1(0, 0) = RR(0, 0, 0) / (1.0 - std::pow(rho, 2)); if(sd_y_est) { - H(0) = new_theta(2 + mu_est); - HH(0) = H(0); + H(0) = std::exp(new_theta(2 + mu_est)); + HH(0) = std::pow(H(0), 2); } if(xreg.n_cols > 0) { @@ -61,37 +61,48 @@ void ar1_lg::update_model(const arma::vec& new_theta, const Rcpp::Function updat double ar1_lg::log_prior_pdf(const arma::vec& x, const Rcpp::Function prior_fn) const { double log_prior = 0.0; - - for(unsigned int i = 0; i < x.n_elem; i++) { + arma::vec pars = x; + + // sigma + pars(1) = std::exp(pars(1)); + // sd_y + pars(2 + mu_est) = std::exp(pars(2 + mu_est)); + // add log-jacobians + log_prior += x(1) + x(2 + mu_est); + + for(unsigned int i = 0; i < pars.n_elem; i++) { switch(prior_distributions(i)) { case 0 : - if (x(i) < prior_parameters(0, i) || x(i) > prior_parameters(1, i)) { + if (pars(i) < prior_parameters(0, i) || pars(i) > prior_parameters(1, i)) { return -std::numeric_limits::infinity(); } break; case 1 : - if (x(i) < 0) { + if (pars(i) < 0) { return -std::numeric_limits::infinity(); } else { - log_prior -= 0.5 * std::pow(x(i) / prior_parameters(0, i), 2); + log_prior -= 0.5 * std::pow(pars(i) / prior_parameters(0, i), 2); } break; case 2 : - log_prior -= 0.5 * std::pow((x(i) - prior_parameters(0, i)) / prior_parameters(1, i), 2); + log_prior -= 0.5 * std::pow((pars(i) - prior_parameters(0, i)) / + prior_parameters(1, i), 2); break; case 3 : // truncated normal - if (x(i) < prior_parameters(2, i) || x(i) > prior_parameters(3, i)) { + if (pars(i) < prior_parameters(2, i) || pars(i) > prior_parameters(3, i)) { return -std::numeric_limits::infinity(); } else { - log_prior -= 0.5 * std::pow((x(i) - prior_parameters(0, i)) / prior_parameters(1, i), 2); + log_prior -= 0.5 * std::pow((pars(i) - prior_parameters(0, i)) / + prior_parameters(1, i), 2); } break; case 4 : // gamma - if (x(i) < 0) { + if (pars(i) < 0) { return -std::numeric_limits::infinity(); } else { - log_prior += (prior_parameters(0, i) - 1) * log(x(i)) - prior_parameters(1, i) * x(i); - + log_prior += (prior_parameters(0, i) - 1) * + log(pars(i)) - prior_parameters(1, i) * pars(i); + } break; } diff --git a/src/model_ar1_ng.cpp b/src/model_ar1_ng.cpp index f33d8c4b..f2a0ae29 100644 --- a/src/model_ar1_ng.cpp +++ b/src/model_ar1_ng.cpp @@ -11,18 +11,19 @@ ar1_ng::ar1_ng(const Rcpp::List model, const unsigned int seed) : void ar1_ng::update_model(const arma::vec& new_theta) { - T(0, 0, 0) = new_theta(0); - R(0, 0, 0) = new_theta(1); + double rho = new_theta(0); + double sigma = std::exp(new_theta(1)); + T(0, 0, 0) = rho; + R(0, 0, 0) = sigma; + RR(0, 0, 0) = std::pow(sigma, 2); if (mu_est) { a1(0) = new_theta(2); - C.fill(new_theta(2) * (1.0 - new_theta(0))); + C.fill(new_theta(2) * (1.0 - rho)); } - P1(0, 0) = std::pow(new_theta(1), 2) / (1.0 - std::pow(new_theta(0), 2)); - - compute_RR(); + P1(0, 0) = RR(0, 0, 0) / (1.0 - std::pow(rho, 2)); if(phi_est) { - phi = new_theta(2 + mu_est); + phi = std::exp(new_theta(2 + mu_est)); } if(xreg.n_cols > 0) { @@ -35,18 +36,19 @@ void ar1_ng::update_model(const arma::vec& new_theta) { } void ar1_ng::update_model(const arma::vec& new_theta, const Rcpp::Function update_fn) { - T(0, 0, 0) = new_theta(0); - R(0, 0, 0) = new_theta(1); + double rho = new_theta(0); + double sigma = std::exp(new_theta(1)); + T(0, 0, 0) = rho; + R(0, 0, 0) = sigma; + RR(0, 0, 0) = std::pow(sigma, 2); if (mu_est) { a1(0) = new_theta(2); - C.fill(new_theta(2) * (1.0 - new_theta(0))); + C.fill(new_theta(2) * (1.0 - rho)); } - P1(0, 0) = std::pow(new_theta(1), 2) / (1.0 - std::pow(new_theta(0), 2)); - - compute_RR(); + P1(0, 0) = RR(0, 0, 0) / (1.0 - std::pow(rho, 2)); if(phi_est) { - phi = new_theta(2 + mu_est); + phi = std::exp(new_theta(2 + mu_est)); } if(xreg.n_cols > 0) { @@ -61,36 +63,50 @@ void ar1_ng::update_model(const arma::vec& new_theta, const Rcpp::Function updat double ar1_ng::log_prior_pdf(const arma::vec& x, const Rcpp::Function prior_fn) const { double log_prior = 0.0; + arma::vec pars = x; + + // sigma + pars(1) = std::exp(pars(1)); + // add log-jacobian + log_prior += x(1); + // phi + if (phi_est) { + pars(2 + mu_est) = std::exp(pars(2 + mu_est)); + log_prior += x(2 + mu_est); + } - for(unsigned int i = 0; i < x.n_elem; i++) { + for(unsigned int i = 0; i < pars.n_elem; i++) { switch(prior_distributions(i)) { case 0 : - if (x(i) < prior_parameters(0, i) || x(i) > prior_parameters(1, i)) { + if (pars(i) < prior_parameters(0, i) || pars(i) > prior_parameters(1, i)) { return -std::numeric_limits::infinity(); } break; case 1 : - if (x(i) < 0) { + if (pars(i) < 0) { return -std::numeric_limits::infinity(); } else { - log_prior -= 0.5 * std::pow(x(i) / prior_parameters(0, i), 2); + log_prior -= 0.5 * std::pow(pars(i) / prior_parameters(0, i), 2); } break; case 2 : - log_prior -= 0.5 * std::pow((x(i) - prior_parameters(0, i)) / prior_parameters(1, i), 2); + log_prior -= 0.5 * std::pow((pars(i) - prior_parameters(0, i)) / + prior_parameters(1, i), 2); break; case 3 : // truncated normal - if (x(i) < prior_parameters(2, i) || x(i) > prior_parameters(3, i)) { + if (pars(i) < prior_parameters(2, i) || pars(i) > prior_parameters(3, i)) { return -std::numeric_limits::infinity(); } else { - log_prior -= 0.5 * std::pow((x(i) - prior_parameters(0, i)) / prior_parameters(1, i), 2); + log_prior -= 0.5 * std::pow((pars(i) - prior_parameters(0, i)) / + prior_parameters(1, i), 2); } break; case 4 : // gamma - if (x(i) < 0) { + if (pars(i) < 0) { return -std::numeric_limits::infinity(); } else { - log_prior += (prior_parameters(0, i) - 1) * log(x(i)) - prior_parameters(1, i) * x(i); + log_prior += (prior_parameters(0, i) - 1) * + log(pars(i)) - prior_parameters(1, i) * pars(i); } break; diff --git a/src/model_bsm_ng.cpp b/src/model_bsm_ng.cpp index 18a41657..7427dc2c 100644 --- a/src/model_bsm_ng.cpp +++ b/src/model_bsm_ng.cpp @@ -91,7 +91,7 @@ double bsm_ng::log_prior_pdf(const arma::vec& x, const Rcpp::Function prior_fn) double log_prior = 0.0; arma::vec pars = x; - if (arma::accu(fixed) < 3 || noise) { + if (arma::accu(fixed) < 3 || noise || phi_est) { pars.subvec(0, pars.n_elem - xreg.n_cols - 1) = arma::exp(pars.subvec(0, pars.n_elem - xreg.n_cols - 1)); // add jacobian diff --git a/src/model_ssm_mng.cpp b/src/model_ssm_mng.cpp index 46ba6ea1..5265106e 100644 --- a/src/model_ssm_mng.cpp +++ b/src/model_ssm_mng.cpp @@ -221,7 +221,7 @@ void ssm_mng::update_scales() { for(unsigned int t = 0; t < n; t++) { for(unsigned int i = 0; i < p; i++) { - if (arma::is_finite(y(i, t))) { + if (std::isfinite(y(i, t))) { switch(distribution(i)) { case 0 : scales(t) += -0.5 * (mode_estimate(i, t) + @@ -361,7 +361,7 @@ arma::vec ssm_mng::log_weights(const unsigned int t, const arma::cube& alpha) c for (unsigned int i = 0; i < alpha.n_slices; i++) { arma::vec simsignal = D.col(t * Dtv) + Z.slice(t * Ztv) * alpha.slice(i).col(t); for(unsigned int j = 0; j < p; j++) { - if (arma::is_finite(y(j, t))) { + if (std::isfinite(y(j, t))) { switch(distribution(j)) { // case 0 : // weights(i) += -0.5 * (simsignal(j) + std::pow(y(j,t) / phi(j), 2.0) * @@ -378,10 +378,10 @@ arma::vec ssm_mng::log_weights(const unsigned int t, const arma::cube& alpha) c std::log(phi(j) + u(j,t) * std::exp(simsignal(j))); break; case 4 : - weights(i) += -phi(j) * simsignal(j) - (y(j,t) * phi(j) * exp(-simsignal(j)) / u(j,t)); + weights(i) -= phi(j) * (simsignal(j) + (y(j,t) * exp(-simsignal(j)) / u(j,t))); break; case 5 : - weights(i) += -0.5 * std::pow((y(j,t) - simsignal(j)) / phi(j), 2.0); + weights(i) -= 0.5 * std::pow((y(j,t) - simsignal(j)) / phi(j), 2.0); break; } weights(i) += @@ -412,7 +412,7 @@ arma::vec ssm_mng::log_obs_density(const unsigned int t, for (unsigned int i = 0; i < alpha.n_slices; i++) { arma::vec simsignal = D.col(t * Dtv) + Z.slice(t * Ztv) * alpha.slice(i).col(t); for(unsigned int j = 0; j < p; j++) { - if (arma::is_finite(y(j, t))) { + if (std::isfinite(y(j, t))) { switch(distribution(j)) { // case 0 : // weights(i) += -0.5 * (simsignal(j) + std::pow(y(j,t) / phi(j), 2.0) * @@ -429,7 +429,7 @@ arma::vec ssm_mng::log_obs_density(const unsigned int t, std::log(phi(j) + u(j,t) * std::exp(simsignal(j))); break; case 4 : - weights(i) += -phi(j) * simsignal(j) - (y(j,t) * phi(j) * exp(-simsignal(j)) / u(j,t)); + weights(i) += -phi(j) * (simsignal(j) + (y(j,t) * exp(-simsignal(j)) / u(j, t))); break; case 5 : weights(i) += -0.5 * std::pow((y(j,t) - simsignal(j)) / phi(j), 2.0); @@ -731,13 +731,13 @@ arma::mat ssm_mng::sample_model(const unsigned int predict_type) { switch(distribution(j)) { case 1: { - std::poisson_distribution<> poisson(u(j,t) * y(j, t)); - if ((u(j,t) * y(j, t)) < poisson.max()) { - y(j, t) = poisson(engine); - } else { - y(j, t) = std::numeric_limits::quiet_NaN(); - } - } + std::poisson_distribution<> poisson(u(j,t) * y(j, t)); + if ((u(j,t) * y(j, t)) < poisson.max()) { + y(j, t) = poisson(engine); + } else { + y(j, t) = std::numeric_limits::quiet_NaN(); + } + } break; case 2: { std::binomial_distribution<> binomial(u(j,t), y(j, t)); @@ -745,9 +745,10 @@ arma::mat ssm_mng::sample_model(const unsigned int predict_type) { } break; case 3: { - std::negative_binomial_distribution<> - negative_binomial(phi(j), phi(j) / (phi(j) + u(j,t) * y(j, t))); - y(j, t) = negative_binomial(engine); + double prob = phi(j) / (phi(j) + u(j,t) * y(j, t)); + std::gamma_distribution<> gamma(phi(j), (1 - prob) / prob); + std::poisson_distribution<> poisson(gamma(engine)); + y(j, t) = poisson(engine); } break; case 4: { @@ -814,9 +815,10 @@ arma::cube ssm_mng::predict_past(const arma::mat& theta_posterior, } break; case 3: { - std::negative_binomial_distribution<> - negative_binomial(phi(j), phi(j) / (phi(j) + u(j,t) * y(j, t))); - y(j, t) = negative_binomial(engine); + double prob = phi(j) / (phi(j) + u(j,t) * y(j, t)); + std::gamma_distribution<> gamma(phi(j), (1 - prob) / prob); + std::poisson_distribution<> poisson(gamma(engine)); + y(j, t) = poisson(engine); } break; case 4: { diff --git a/src/model_ssm_nlg.cpp b/src/model_ssm_nlg.cpp index 91acf2a1..68b91b2c 100644 --- a/src/model_ssm_nlg.cpp +++ b/src/model_ssm_nlg.cpp @@ -66,7 +66,7 @@ void ssm_nlg::approximate() { // initial approximation is based on EKF (at and att) approximate_by_ekf(); mode_estimate = approx_model.fast_smoother().head_cols(n); - if (!arma::is_finite(mode_estimate)) { + if (!mode_estimate.is_finite()) { return; } double ll; @@ -112,7 +112,7 @@ void ssm_nlg::approximate() { double ll_new = log_signal_pdf(mode_estimate_new); abs_diff = ll_new - ll; rel_diff = abs_diff / std::abs(ll); - if (!arma::is_finite(mode_estimate_new) || !arma::is_finite(ll_new)) { + if (!mode_estimate_new.is_finite() || !std::isfinite(ll_new)) { mode_estimate.fill(std::numeric_limits::infinity()); return; } @@ -131,7 +131,7 @@ void ssm_nlg::approximate() { abs_diff = ll_new - ll; rel_diff = abs_diff / std::abs(ll); ii++; - if (!arma::is_finite(mode_estimate) || !arma::is_finite(ll_new)) { + if (!mode_estimate.is_finite() || !std::isfinite(ll_new)) { mode_estimate.fill(std::numeric_limits::infinity()); return; } @@ -353,7 +353,7 @@ double ssm_nlg::ekf(arma::mat& at, arma::mat& att, arma::cube& Pt, arma::cube& P Kt = Pt.slice(t) * Zg.t() * inv_cholF * inv_cholF.t(); arma::vec atthat_new = at.col(t) + Kt * vt; - diff = arma::mean(arma::square(atthat-atthat_new)); + diff = arma::mean(arma::square(atthat - atthat_new)); atthat = atthat_new; } att.col(t) = atthat; @@ -448,14 +448,14 @@ double ssm_nlg::ekf_loglik() const { vt = y.col(t) - Z_fn(t, atthat, theta, known_params, known_tv_params) - - Zg * (at.col(t) - atthat); + Zg * (at - atthat); vt.rows(na_y).zeros(); inv_cholF = arma::inv(arma::trimatu(cholF)); Kt = Pt * Zg.t() * inv_cholF * inv_cholF.t(); arma::vec atthat_new = at + Kt * vt; - diff = arma::mean(arma::square(atthat-atthat_new)); + diff = arma::mean(arma::square(atthat - atthat_new)); atthat = atthat_new; } att = atthat; @@ -527,6 +527,7 @@ double ssm_nlg::ekf_smoother(arma::mat& at, arma::cube& Pt) const { arma::mat inv_cholF = arma::inv(arma::trimatu(cholF)); ZFinv.slice(t) = Zg.t() * inv_cholF * inv_cholF.t(); Kt.slice(t) = Pt.slice(t) * ZFinv.slice(t); + arma::vec atthat = at.col(t) + Kt.slice(t) * vt.col(t); double diff = 1.0; @@ -558,11 +559,12 @@ double ssm_nlg::ekf_smoother(arma::mat& at, arma::cube& Pt) const { vt.rows(na_y).zeros(); inv_cholF = arma::inv(arma::trimatu(cholF)); - Kt.slice(t) = Pt.slice(t) * Zg.t() * inv_cholF * inv_cholF.t(); + ZFinv.slice(t) = Zg.t() * inv_cholF * inv_cholF.t(); + Kt.slice(t) = Pt.slice(t) * ZFinv.slice(t); arma::vec atthat_new = at.col(t) + Kt.slice(t) * vt.col(t); - diff = arma::mean(arma::square(atthat-atthat_new)); + diff = arma::mean(arma::square(atthat - atthat_new)); atthat = atthat_new; } att.col(t) = atthat; @@ -603,9 +605,13 @@ double ssm_nlg::ekf_smoother(arma::mat& at, arma::cube& Pt) const { at.col(t) += Pt.slice(t) * rt; Pt.slice(t) -= Pt.slice(t) * Nt * Pt.slice(t); } + return logLik; } +// does not actually use the fast state smoothing recursions of 4.6.2 of DK2021 +// fast here means only that it does not compute the smoothed variances +// in gaussian case fast means fast state smoothing... double ssm_nlg::ekf_fast_smoother(arma::mat& at) const { at.col(0) = a1_fn(theta, known_params); @@ -657,8 +663,8 @@ double ssm_nlg::ekf_fast_smoother(arma::mat& at) const { ZFinv.slice(t) = Zg.t() * inv_cholF * inv_cholF.t(); Kt.slice(t) = Pt.slice(t) * ZFinv.slice(t); - arma::vec atthat = at.col(t) + Kt.slice(t) * vt.col(t); + double diff = 1.0; unsigned int i = 0; while (diff > 1e-4 && i < iekf_iter) { @@ -688,10 +694,11 @@ double ssm_nlg::ekf_fast_smoother(arma::mat& at) const { vt.rows(na_y).zeros(); inv_cholF = arma::inv(arma::trimatu(cholF)); - Kt.slice(t) = Pt.slice(t) * Zg.t() * inv_cholF * inv_cholF.t(); + ZFinv.slice(t) = Zg.t() * inv_cholF * inv_cholF.t(); + Kt.slice(t) = Pt.slice(t) * ZFinv.slice(t); arma::vec atthat_new = at.col(t) + Kt.slice(t) * vt.col(t); - diff = arma::mean(arma::square(atthat-atthat_new)); + diff = arma::mean(arma::square(atthat - atthat_new)); atthat = atthat_new; } att.col(t) = atthat; diff --git a/src/model_ssm_sde.cpp b/src/model_ssm_sde.cpp index e7945bc6..1f97587b 100644 --- a/src/model_ssm_sde.cpp +++ b/src/model_ssm_sde.cpp @@ -31,7 +31,7 @@ double ssm_sde::bsf_filter(const unsigned int nsim, arma::vec normalized_weights(nsim); double loglik = 0.0; - if(arma::is_finite(y(0))) { + if (std::isfinite(y(0))) { weights.col(0) = log_obs_density(y(0), alpha.tube(0, 0), theta); double max_weight = weights.col(0).max(); weights.col(0) = arma::exp(weights.col(0) - max_weight); @@ -61,7 +61,7 @@ double ssm_sde::bsf_filter(const unsigned int nsim, drift, diffusion, ddiffusion, positive, coarse_engine); } - if ((t < (n - 1)) && arma::is_finite(y(t + 1))) { + if ((t < (n - 1)) && std::isfinite(y(t + 1))) { weights.col(t + 1) = log_obs_density(y(t + 1), alpha.tube(0, t + 1), theta); double max_weight = weights.col(t + 1).max(); diff --git a/src/model_ssm_ulg.cpp b/src/model_ssm_ulg.cpp index d0a62b41..d2a558fc 100644 --- a/src/model_ssm_ulg.cpp +++ b/src/model_ssm_ulg.cpp @@ -122,7 +122,7 @@ double ssm_ulg::log_likelihood() const { for (unsigned int t = 0; t < n; t++) { double F = arma::as_scalar(Z.col(t * Ztv).t() * Pt * Z.col(t * Ztv) + HH(t * Htv)); - if (arma::is_finite(y_tmp(t)) && F > zero_tol) { + if (std::isfinite(y_tmp(t)) && F > zero_tol) { double v = arma::as_scalar(y_tmp(t) - D(t * Dtv) - Z.col(t * Ztv).t() * at); arma::vec K = Pt * Z.col(t * Ztv) / F; at = C.col(t * Ctv) + T.slice(t * Ttv) * (at + K * v); @@ -171,7 +171,7 @@ arma::cube ssm_ulg::simulate_states(const unsigned int nsim, const bool use_anti } aplus.col(0) = a1 + L_P1 * um; for (unsigned int t = 0; t < n; t++) { - if (arma::is_finite(y(t))) { + if (std::isfinite(y(t))) { y(t) = xbeta(t) + D(t * Dtv) + arma::as_scalar(Z.col(t * Ztv).t() * aplus.col(t)) + H(t * Htv) * normal(engine); @@ -199,7 +199,7 @@ arma::cube ssm_ulg::simulate_states(const unsigned int nsim, const bool use_anti } aplus.col(0) = a1 + L_P1 * um; for (unsigned int t = 0; t < n; t++) { - if (arma::is_finite(y(t))) { + if (std::isfinite(y(t))) { y(t) = xbeta(t) + D(t * Dtv) + arma::as_scalar(Z.col(t * Ztv).t() * aplus.col(t)) + H(t * Htv) * normal(engine); @@ -227,7 +227,7 @@ arma::cube ssm_ulg::simulate_states(const unsigned int nsim, const bool use_anti } asim.slice(0).col(0) = L_P1 * um; for (unsigned int t = 0; t < n; t++) { - if (arma::is_finite(y(t))) { + if (std::isfinite(y(t))) { y(t) -= arma::as_scalar(Z.col(t * Ztv).t() * asim.slice(0).col(t)) + H(t * Htv) * normal(engine); } @@ -267,7 +267,7 @@ arma::mat ssm_ulg::fast_smoother() const { for (unsigned int t = 0; t < n; t++) { Ft(t) = arma::as_scalar(Z.col(t * Ztv).t() * Pt * Z.col(t * Ztv) + HH(t * Htv)); - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol) { + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol) { Kt.col(t) = Pt * Z.col(t * Ztv) / Ft(t); vt(t) = arma::as_scalar(y_tmp(t) - D(t * Dtv) - Z.col(t * Ztv).t() * at.col(t)); at.col(t + 1) = C.col(t * Ctv) + T.slice(t * Ttv) * (at.col(t) + Kt.col(t) * vt(t)); @@ -283,14 +283,14 @@ arma::mat ssm_ulg::fast_smoother() const { arma::mat rt(m, n); rt.col(n - 1).zeros(); for (int t = (n - 1); t > 0; t--) { - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol){ + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol){ arma::mat L = T.slice(t * Ttv) * (arma::eye(m, m) - Kt.col(t) * Z.col(t * Ztv).t()); rt.col(t - 1) = Z.col(t * Ztv) / Ft(t) * vt(t) + L.t() * rt.col(t); } else { rt.col(t - 1) = T.slice(t * Ttv).t() * rt.col(t); } } - if (arma::is_finite(y(0)) && Ft(0) > zero_tol){ + if (std::isfinite(y(0)) && Ft(0) > zero_tol){ arma::mat L = T.slice(0) * (arma::eye(m, m) - Kt.col(0) * Z.col(0).t()); at.col(0) = a1 + P1 * (Z.col(0) / Ft(0) * vt(0) + L.t() * rt.col(0)); } else { @@ -328,7 +328,7 @@ arma::mat ssm_ulg::fast_smoother(const arma::vec& Ft, const arma::mat& Kt, } for (unsigned int t = 0; t < n; t++) { - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol) { + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol) { vt(t) = arma::as_scalar(y_tmp(t) - D(t * Dtv) - Z.col(t * Ztv).t() * at.col(t)); at.col(t + 1) = C.col(t * Ctv) + T.slice(t * Ttv) * (at.col(t) + Kt.col(t) * vt(t)); } else { @@ -340,13 +340,13 @@ arma::mat ssm_ulg::fast_smoother(const arma::vec& Ft, const arma::mat& Kt, rt.col(n - 1).zeros(); for (int t = (n - 1); t > 0; t--) { - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol){ + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol){ rt.col(t - 1) = Z.col(t * Ztv) / Ft(t) * vt(t) + Lt.slice(t).t() * rt.col(t); } else { rt.col(t - 1) = T.slice(t * Ttv).t() * rt.col(t); } } - if (arma::is_finite(y(0)) && Ft(0) > zero_tol){ + if (std::isfinite(y(0)) && Ft(0) > zero_tol){ arma::mat L = T.slice(0) * (arma::eye(m, m) - Kt.col(0) * Z.col(0).t()); at.col(0) = a1 + P1 * (Z.col(0) / Ft(0) * vt(0) + L.t() * rt.col(0)); } else { @@ -376,7 +376,7 @@ arma::mat ssm_ulg::fast_precomputing_smoother(arma::vec& Ft, arma::mat& Kt, } for (unsigned int t = 0; t < n; t++) { Ft(t) = arma::as_scalar(Z.col(t * Ztv).t() * Pt * Z.col(t * Ztv) + HH(t * Htv)); - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol) { + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol) { Kt.col(t) = Pt * Z.col(t * Ztv) / Ft(t); vt(t) = arma::as_scalar(y_tmp(t) - D(t * Dtv) - Z.col(t * Ztv).t() * at.col(t)); at.col(t + 1) = C.col(t * Ctv) + T.slice(t * Ttv) * (at.col(t) + Kt.col(t) * vt(t)); @@ -394,14 +394,14 @@ arma::mat ssm_ulg::fast_precomputing_smoother(arma::vec& Ft, arma::mat& Kt, rt.col(n - 1).zeros(); for (int t = (n - 1); t > 0; t--) { - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol){ + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol){ Lt.slice(t) = T.slice(t * Ttv) * (arma::eye(m, m) - Kt.col(t) * Z.col(t * Ztv).t()); rt.col(t - 1) = Z.col(t * Ztv) / Ft(t) * vt(t) + Lt.slice(t).t() * rt.col(t); } else { rt.col(t - 1) = T.slice(t * Ttv).t() * rt.col(t); } } - if (arma::is_finite(y_tmp(0)) && Ft(0) > zero_tol){ + if (std::isfinite(y_tmp(0)) && Ft(0) > zero_tol){ arma::mat L = T.slice(0) * (arma::eye(m, m) - Kt.col(0) * Z.col(0).t()); at.col(0) = a1 + P1 * (Z.col(0) / Ft(0) * vt(0) + L.t() * rt.col(0)); } else { @@ -432,7 +432,7 @@ void ssm_ulg::smoother_ccov(arma::mat& at, arma::cube& Pt, arma::cube& ccov) con for (unsigned int t = 0; t < n; t++) { Ft(t) = arma::as_scalar(Z.col(t * Ztv).t() * Pt.slice(t) * Z.col(t * Ztv) + HH(t * Htv)); - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol) { + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol) { Kt.col(t) = Pt.slice(t) * Z.col(t * Ztv) / Ft(t); vt(t) = arma::as_scalar(y_tmp(t) - D(t * Dtv) - Z.col(t * Ztv).t() * at.col(t)); at.col(t + 1) = C.col(t * Ctv) + T.slice(t * Ttv) * (at.col(t) + Kt.col(t) * vt(t)); @@ -454,7 +454,7 @@ void ssm_ulg::smoother_ccov(arma::mat& at, arma::cube& Pt, arma::cube& ccov) con arma::mat Nt(m, m, arma::fill::zeros); for (int t = (n - 1); t >= 0; t--) { - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol){ + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol){ arma::mat L = T.slice(t * Ttv) * (arma::eye(m, m) - Kt.col(t) * Z.col(t * Ztv).t()); //P[t+1] stored to ccov_t ccov.slice(t) = Pt.slice(t) * L.t() * (arma::eye(m, m) - Nt * ccov.slice(t)); @@ -489,7 +489,7 @@ double ssm_ulg::filter(arma::mat& at, arma::mat& att, arma::cube& Pt, for (unsigned int t = 0; t < n; t++) { double F = arma::as_scalar(Z.col(t * Ztv).t() * Pt.slice(t) * Z.col(t * Ztv) + HH(t * Htv)); - if (arma::is_finite(y_tmp(t)) && F > zero_tol) { + if (std::isfinite(y_tmp(t)) && F > zero_tol) { double v = arma::as_scalar(y_tmp(t) - D(t * Dtv) - Z.col(t * Ztv).t() * at.col(t)); arma::vec K = Pt.slice(t) * Z.col(t * Ztv) / F; att.col(t) = at.col(t) + K * v; @@ -524,7 +524,7 @@ void ssm_ulg::smoother(arma::mat& at, arma::cube& Pt) const { for (unsigned int t = 0; t < n; t++) { Ft(t) = arma::as_scalar(Z.col(t * Ztv).t() * Pt.slice(t) * Z.col(t * Ztv) + HH(t * Htv)); - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol) { + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol) { Kt.col(t) = Pt.slice(t) * Z.col(t * Ztv) / Ft(t); vt(t) = arma::as_scalar(y_tmp(t) - D(t * Dtv) - Z.col(t * Ztv).t() * at.col(t)); at.col(t + 1) = C.col(t * Ctv) + T.slice(t * Ttv) * (at.col(t) + Kt.col(t) * vt(t)); @@ -541,7 +541,7 @@ void ssm_ulg::smoother(arma::mat& at, arma::cube& Pt) const { arma::mat Nt(m, m, arma::fill::zeros); for (int t = (n - 1); t >= 0; t--) { - if (arma::is_finite(y_tmp(t)) && Ft(t) > zero_tol){ + if (std::isfinite(y_tmp(t)) && Ft(t) > zero_tol){ arma::mat L = T.slice(t * Ttv) * (arma::eye(m, m) - Kt.col(t) * Z.col(t * Ztv).t()); rt = Z.col(t * Ztv) / Ft(t) * vt(t) + L.t() * rt; Nt = arma::symmatu(Z.col(t * Ztv) * Z.col(t * Ztv).t() / Ft(t) + L.t() * Nt * L); @@ -572,7 +572,7 @@ double ssm_ulg::bsf_filter(const unsigned int nsim, arma::cube& alpha, arma::vec normalized_weights(nsim); double loglik = 0.0; - if(arma::is_finite(y(0))) { + if(std::isfinite(y(0))) { for (unsigned int i = 0; i < nsim; i++) { double mu = arma::as_scalar(D(0) + Z.col(0).t() * @@ -616,7 +616,7 @@ double ssm_ulg::bsf_filter(const unsigned int nsim, arma::cube& alpha, T.slice(t * Ttv) * alphatmp.col(i) + R.slice(t * Rtv) * uk; } - if ((t < (n - 1)) && arma::is_finite(y(t + 1))) { + if ((t < (n - 1)) && std::isfinite(y(t + 1))) { for (unsigned int i = 0; i < nsim; i++) { double mu = arma::as_scalar(D((t + 1) * Dtv) + Z.col(Ztv * (t + 1)).t() * alpha.slice(i).col(t + 1)); diff --git a/src/model_ssm_ung.cpp b/src/model_ssm_ung.cpp index b3b24347..6bdc6b16 100644 --- a/src/model_ssm_ung.cpp +++ b/src/model_ssm_ung.cpp @@ -245,7 +245,7 @@ void ssm_ung::update_scales() { switch(distribution) { case 0 : for(unsigned int t = 0; t < n; t++) { - if (arma::is_finite(y(t))) { + if (std::isfinite(y(t))) { scales(t) = -0.5 * (mode_estimate(t) + std::pow(y(t) / phi, 2.0) * std::exp(-mode_estimate(t))) + 0.5 * std::pow((approx_model.y(t) - mode_estimate(t)) / approx_model.H(t), 2.0); @@ -254,7 +254,7 @@ void ssm_ung::update_scales() { break; case 1 : for(unsigned int t = 0; t < n; t++) { - if (arma::is_finite(y(t))) { + if (std::isfinite(y(t))) { scales(t) = y(t) * mode_estimate(t) - u(t) * std::exp(mode_estimate(t)) + 0.5 * std::pow((approx_model.y(t) - mode_estimate(t)) / approx_model.H(t), 2.0); @@ -263,7 +263,7 @@ void ssm_ung::update_scales() { break; case 2 : for(unsigned int t = 0; t < n; t++) { - if (arma::is_finite(y(t))) { + if (std::isfinite(y(t))) { scales(t) = y(t) * mode_estimate(t) - u(t) * std::log1p(std::exp(mode_estimate(t))) + 0.5 * std::pow((approx_model.y(t) - mode_estimate(t)) / approx_model.H(t), 2.0); @@ -272,7 +272,7 @@ void ssm_ung::update_scales() { break; case 3 : for(unsigned int t = 0; t < n; t++) { - if (arma::is_finite(y(t))) { + if (std::isfinite(y(t))) { scales(t) = y(t) * mode_estimate(t) - (y(t) + phi) * std::log(phi + u(t) * std::exp(mode_estimate(t))) + @@ -282,8 +282,8 @@ void ssm_ung::update_scales() { break; case 4 : for(unsigned int t = 0; t < n; t++) { - if (arma::is_finite(y(t))) { - scales(t) = -phi * mode_estimate(t) - (y(t) * phi * exp(-mode_estimate(t)) / u(t)) + + if (std::isfinite(y(t))) { + scales(t) = -phi * (mode_estimate(t) + (y(t) * exp(-mode_estimate(t)) / u(t))) + 0.5 * std::pow((approx_model.y(t) - mode_estimate(t)) / approx_model.H(t), 2.0); } } @@ -388,7 +388,7 @@ arma::vec ssm_ung::log_weights( arma::vec weights(alpha.n_slices, arma::fill::zeros); - if (arma::is_finite(y(t))) { + if (std::isfinite(y(t))) { switch(distribution) { case 0 : for (unsigned int i = 0; i < alpha.n_slices; i++) { @@ -426,7 +426,7 @@ arma::vec ssm_ung::log_weights( for (unsigned int i = 0; i < alpha.n_slices; i++) { double simsignal = arma::as_scalar(D(t * Dtv) + Z.col(t * Ztv).t() * alpha.slice(i).col(t) + xbeta(t)); - weights(i) = -phi * simsignal - (y(t) * phi * exp(-simsignal) / u(t)) + + weights(i) = -phi * (simsignal + (y(t) * exp(-simsignal) / u(t))) + 0.5 * std::pow((approx_model.y(t) - simsignal) / approx_model.H(t), 2.0); } break; @@ -449,7 +449,7 @@ arma::vec ssm_ung::log_obs_density(const unsigned int t, arma::vec weights(alpha.n_slices, arma::fill::zeros); - if (arma::is_finite(y(t))) { + if (std::isfinite(y(t))) { switch(distribution) { case 0 : for (unsigned int i = 0; i < alpha.n_slices; i++) { @@ -483,7 +483,7 @@ arma::vec ssm_ung::log_obs_density(const unsigned int t, for (unsigned int i = 0; i < alpha.n_slices; i++) { double simsignal = arma::as_scalar(D(t * Dtv) + Z.col(t * Ztv).t() * alpha.slice(i).col(t) + xbeta(t)); - weights(i) = -phi * simsignal - (y(t) * phi * exp(-simsignal) / u(t)); + weights(i) = -phi * (simsignal + (y(t) * exp(-simsignal) / u(t))); } break; @@ -546,7 +546,7 @@ double ssm_ung::psi_filter(const unsigned int nsim, arma::cube& alpha, std::uniform_real_distribution<> unif(0.0, 1.0); arma::vec normalized_weights(nsim); double loglik = 0.0; - if(arma::is_finite(y(0))) { + if(std::isfinite(y(0))) { weights.col(0) = log_weights(0, alpha) - scales(0); double max_weight = weights.col(0).max(); @@ -586,7 +586,7 @@ double ssm_ung::psi_filter(const unsigned int nsim, arma::cube& alpha, Ct.slice(t + 1) * (alphatmp.col(i) - alphahat.col(t)) + Vt.slice(t + 1) * um; } - if ((t < (n - 1)) && arma::is_finite(y(t + 1))) { + if ((t < (n - 1)) && std::isfinite(y(t + 1))) { weights.col(t + 1) = log_weights(t + 1, alpha) - scales(t + 1); double max_weight = weights.col(t + 1).max(); @@ -630,7 +630,7 @@ double ssm_ung::bsf_filter(const unsigned int nsim, arma::cube& alpha, arma::vec normalized_weights(nsim); double loglik = 0.0; - if(arma::is_finite(y(0))) { + if(std::isfinite(y(0))) { weights.col(0) = log_obs_density(0, alpha); double max_weight = weights.col(0).max(); weights.col(0) = arma::exp(weights.col(0) - max_weight); @@ -669,7 +669,7 @@ double ssm_ung::bsf_filter(const unsigned int nsim, arma::cube& alpha, T.slice(t * Ttv) * alphatmp.col(i) + R.slice(t * Rtv) * uk; } - if ((t < (n - 1)) && arma::is_finite(y(t + 1))) { + if ((t < (n - 1)) && std::isfinite(y(t + 1))) { weights.col(t + 1) = log_obs_density(t + 1, alpha); double max_weight = weights.col(t + 1).max(); @@ -809,9 +809,13 @@ arma::mat ssm_ung::sample_model(const unsigned int predict_type) { break; case 3: for (unsigned int t = 0; t < n; t++) { - std::negative_binomial_distribution<> - negative_binomial(phi, phi / (phi + u(t) * y(0, t))); - y(0, t) = negative_binomial(engine); + // std::negative_binomial_distribution<> + // negative_binomial(phi, phi / (phi + u(t) * y(0, t))); + // y(0, t) = negative_binomial(engine); + double prob = phi / (phi + u(t) * y(0, t)); + std::gamma_distribution<> gamma(phi, (1 - prob) / prob); + std::poisson_distribution<> poisson(gamma(engine)); + y(0, t) = poisson(engine); } break; case 4: @@ -897,9 +901,10 @@ arma::cube ssm_ung::predict_past(const arma::mat& theta_posterior, break; case 3: for (unsigned int t = 0; t < n; t++) { - std::negative_binomial_distribution<> - negative_binomial(phi, phi / (phi + u(t) * y(0, t))); - y(0, t) = negative_binomial(engine); + double prob = phi / (phi + u(t) * y(0, t)); + std::gamma_distribution<> gamma(phi, (1 - prob) / prob); + std::poisson_distribution<> poisson(gamma(engine)); + y(0, t) = poisson(engine); } break; case 4: diff --git a/tests/test_all.R b/tests/test_all.R index cfc7d472..e93928f0 100644 --- a/tests/test_all.R +++ b/tests/test_all.R @@ -1,2 +1,4 @@ +Sys.setenv("OMP_NUM_THREADS" = 2) + library("testthat") test_check("bssm") diff --git a/tests/testthat/test_approx.R b/tests/testthat/test_approx.R index 17dccb22..be484da5 100644 --- a/tests/testthat/test_approx.R +++ b/tests/testthat/test_approx.R @@ -1,8 +1,10 @@ -context("Test Gaussian approximation") +#' @srrstats {G5.4, G5.4a, G5.4b, G5.4c, G5.9b} Tests that the approximation +#' coincides with KFAS and in GLM case results coincide with the glm. +context("Test Gaussian approximation") -test_that("Gaussian approximation results of bssm and KFAS coincide",{ - library(KFAS) +test_that("Gaussian approximation results of bssm and KFAS coincide", { + suppressWarnings(library(KFAS)) set.seed(123) model_KFAS <- SSModel(rpois(10, exp(2)) ~ SSMtrend(2, Q = list(1, 1), P1 = diag(100, 2)), distribution = "poisson") @@ -10,41 +12,72 @@ test_that("Gaussian approximation results of bssm and KFAS coincide",{ sd_slope = 1, distribution = "poisson"), NA) approx_KFAS <- approxSSM(model_KFAS) expect_error(approx_bssm <- gaussian_approx(model_bssm), NA) - all.equal(c(approx_bssm$H^2),c(approx_KFAS$H)) + expect_equivalent(c(approx_bssm$H^2), c(approx_KFAS$H)) expect_error(alphahat <- fast_smoother(approx_bssm), NA) expect_equivalent(KFS(approx_KFAS)$alphahat, alphahat) expect_equivalent(logLik(approx_KFAS), logLik(approx_bssm)) + + model_KFAS <- SSModel(rbinom(10, 10, 0.5) ~ SSMtrend(2, Q = list(1, 1), + P1 = diag(100, 2)), u = 10, distribution = "binomial") + expect_error(model_bssm <- bsm_ng(model_KFAS$y, sd_level = 1, + sd_slope = 1, distribution = "binomial", u = 10), NA) + approx_KFAS <- approxSSM(model_KFAS) + expect_error(approx_bssm <- gaussian_approx(model_bssm), NA) + expect_equivalent(c(approx_bssm$H^2), c(approx_KFAS$H)) + expect_error(alphahat <- fast_smoother(approx_bssm), NA) + expect_equivalent(KFS(approx_KFAS)$alphahat, alphahat) + expect_equivalent(logLik(approx_KFAS), logLik(approx_bssm)) + + model_bssm$initial_mode[] <- model_bssm$initial_mode + rnorm(10, sd = 0.1) + expect_equivalent(logLik(gaussian_approx(model_bssm)), + logLik(approx_bssm), tol = 0.001) + }) -test_that("Gaussian approximation works for SV model",{ +test_that("Gaussian approximation works for SV model", { set.seed(123) - expect_error(model_bssm <- svm(rnorm(5), sigma = uniform(1,0,10), rho = uniform(0.950, 0, 1), - sd_ar = uniform(0.1,0,1)), NA) - expect_error(approx_bssm <- gaussian_approx(model_bssm, max_iter = 2, conv_tol = 1e-8), NA) + expect_error(model_bssm <- svm(rnorm(5), sigma = uniform(1, 0, 10), + rho = uniform(0.950, 0, 1), + sd_ar = uniform(0.1, 0, 1)), NA) + expect_error(approx_bssm <- gaussian_approx(model_bssm, max_iter = 2, + conv_tol = 1e-8), NA) expect_equivalent(c(-1.47548927809174, -11.2190916117862, - 0.263154138901814, -121.519769682058, -36.0386937004332), approx_bssm$y[1:5]) + 0.263154138901814, -121.519769682058, -36.0386937004332), + approx_bssm$y[1:5]) expect_equivalent(c(2.01061310553144, 4.84658294043645, 0.712674409714633, 15.6217737012134, 8.54936618861792), approx_bssm$H[1:5]) expect_equivalent(c(-0.0999179077423753, -0.101594935319188, - -0.0985572218431492, -0.103275329248674, -0.103028083292436), fast_smoother(approx_bssm)[1:5]) + -0.0985572218431492, -0.103275329248674, -0.103028083292436), + fast_smoother(approx_bssm)[1:5]) + + model_bssm2 <- model_bssm + model_bssm2$initial_mode[] <- model_bssm$initial_mode + rnorm(5, sd = 0.1) + expect_equivalent(logLik(gaussian_approx(model_bssm)), + logLik(gaussian_approx(model_bssm2)), tol = 0.001) }) -test_that("results for poisson GLM are equal to glm function",{ - d <- data.frame(treatment = gl(3,3), outcome = gl(3,1,9), counts = c(18,17,15,20,10,20,25,13,12)) - glm_poisson <- glm(counts ~ outcome + treatment, data = d, family = poisson()) +test_that("results for poisson GLM are equal to glm function", { + d <- data.frame(treatment = gl(3, 3), outcome = gl(3, 1, 9), + counts = c(18, 17, 15, 20, 10, 20, 25, 13, 12)) + glm_poisson <- glm(counts ~ outcome + treatment, data = d, + family = poisson()) xreg <- model.matrix(~ outcome + treatment, data = d) - expect_error(model_poisson1 <- ssm_ung(d$counts, Z = t(xreg), T = diag(5), R = diag(0, 5), - P1 = diag(1e7, 5), distribution = 'poisson', state_names = colnames(xreg)), NA) + expect_error(model_poisson1 <- ssm_ung(d$counts, Z = t(xreg), T = diag(5), + R = diag(0, 5), + P1 = diag(1e7, 5), distribution = "poisson", + state_names = colnames(xreg)), NA) expect_error(sm <- smoother(model_poisson1), NA) - expect_equal(sm$alphahat[1,], coef(glm_poisson)) - expect_equal(sm$V[,,1], vcov(glm_poisson)) + expect_equal(sm$alphahat[1, ], coef(glm_poisson)) + expect_equal(sm$V[, , 1], vcov(glm_poisson)) xreg <- model.matrix(~ outcome + treatment, data = d)[, -1] - expect_error(model_poisson2 <- bsm_ng(d$counts, sd_level = 0, xreg = xreg, P1=matrix(1e7), - beta = normal(coef(glm_poisson)[-1], 0, 10), distribution = 'poisson'), NA) - expect_equivalent(smoother(model_poisson2)$alphahat[1,], coef(glm_poisson)[1]) + expect_error(model_poisson2 <- bsm_ng(d$counts, sd_level = 0, xreg = xreg, + P1 = matrix(1e7), + beta = normal(coef(glm_poisson)[-1], 0, 10), distribution = "poisson"), NA) + expect_equivalent(smoother(model_poisson2)$alphahat[1, ], + coef(glm_poisson)[1]) model_poisson3 <- model_poisson1 model_poisson3$P1[] <- 0 @@ -53,7 +86,7 @@ test_that("results for poisson GLM are equal to glm function",{ model_poisson4 <- ssm_ung(d$counts, Z = 1, T = 1, R = 0, D = t(model_poisson2$xreg %*% model_poisson2$beta), - P1 = 1e7, distribution = 'poisson') + P1 = 1e7, distribution = "poisson") expect_equivalent(gaussian_approx(model_poisson1)$y, gaussian_approx(model_poisson2)$y) @@ -75,17 +108,17 @@ test_that("results for poisson GLM are equal to glm function",{ }) -test_that("results for binomial GLM are equal to glm function",{ +test_that("results for binomial GLM are equal to glm function", { ldose <- rep(0:5, 2) numdead <- c(1, 4, 9, 13, 18, 20, 0, 2, 6, 10, 12, 16) sex <- factor(rep(c("M", "F"), c(6, 6))) - SF <- cbind(numdead, numalive = 20-numdead) + SF <- cbind(numdead, numalive = 20 - numdead) glm_binomial <- glm(SF ~ sex * ldose, family = binomial) xreg <- model.matrix(~ sex * ldose) expect_error(model_binomial <- ssm_ung(numdead, Z = t(xreg), T = diag(4), R = diag(0, 4), P1 = diag(1e5, 4), - distribution = 'binomial', u = 20, state_names = colnames(xreg)), NA) + distribution = "binomial", u = 20, state_names = colnames(xreg)), NA) expect_error(sm <- smoother(model_binomial), NA) # non-exact diffuse initialization is numerically difficult... expect_equal(sm$alphahat[1, ], coef(glm_binomial), tolerance = 1e-5) @@ -94,34 +127,75 @@ test_that("results for binomial GLM are equal to glm function",{ }) -test_that("state estimates for negative binomial GLM are equal to glm function",{ +test_that("state estimates for NB GLM are equal to glm function", { library(MASS) set.seed(123) offs <- quine$Days + sample(10:20, size = nrow(quine), replace = TRUE) glm_nb <- glm.nb(Days ~ 1 + offset(log(offs)), data = quine) expect_error(model_nb <- bsm_ng(quine$Days, u = offs, sd_level = 0, P1 = matrix(1e7), phi = glm_nb$theta, - distribution = 'negative binomial'), NA) + distribution = "negative binomial"), NA) approx_model <- gaussian_approx(model_nb, conv_tol = 1e-12) expect_error(sm <- smoother(approx_model), NA) expect_equivalent(sm$alphahat[1], unname(coef(glm_nb)[1])) }) -test_that("multivariate iid model gives same results as two univariate models", { +test_that("Two iid model gives same results as two univariate models", { set.seed(1) y <- matrix(rbinom(20, size = 10, prob = plogis(rnorm(20, sd = 0.5))), 10, 2) expect_error(model <- ssm_mng(y, Z = diag(2), phi = 2, T = diag(2), R = array(diag(0.5, 2), c(2, 2, 1)), a1 = matrix(0, 2, 1), P1 = diag(2), distribution = "negative binomial", - init_theta = c(0,0)), NA) - expect_error(model1 <- ssm_ung(y[,1], Z = 1, phi = 2, + init_theta = c(0, 0)), NA) + expect_error(model1 <- ssm_ung(y[, 1], Z = 1, phi = 2, T = 1, R = 0.5, P1 = 1, distribution = "negative binomial", init_theta = 0), NA) - expect_error(model2 <- ssm_ung(y[,2], Z = 1, phi = 2, + expect_error(model2 <- ssm_ung(y[, 2], Z = 1, phi = 2, T = 1, R = 0.5, P1 = 1, distribution = "negative binomial", init_theta = 0), NA) expect_equivalent(gaussian_approx(model, conv_tol = 1e-12)$y, cbind(gaussian_approx(model1, conv_tol = 1e-12)$y, gaussian_approx(model2, conv_tol = 1e-12)$y), tol = 1e-6) }) + +test_that("Gaussian approximation works for nonlinear models", { + + skip_on_cran() + + pntrs <- cpp_example_model("nlg_linear_gaussian") + set.seed(1) + y <- cumsum(rnorm(10)) + rnorm(10) + model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(log_H = 0), log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state") + model_gaussian <- bsm_lg(y, sd_y = 1, sd_level = 1, P1 = 1) + expect_equal( + logLik(model_nlg, method = "ekf", particles = 0), + logLik(gaussian_approx(model_nlg))) + expect_equal(logLik(model_gaussian), logLik(gaussian_approx(model_nlg))) + + set.seed(1) + n <- 30 + x <- y <- numeric(n) + y[1] <- rnorm(1, exp(x[1]), 0.1) + for(i in 1:(n-1)) { + x[i+1] <- rnorm(1, sin(x[i]), 0.1) + y[i+1] <- rnorm(1, exp(x[i+1]), 0.1) + } + y[2:5] <- NA + pntrs <- cpp_example_model("nlg_sin_exp") + + expect_error(model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(log_H = log(0.1), log_R = log(0.1)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state"), NA) + + expect_equal(gaussian_approx(model_nlg), + gaussian_approx(model_nlg, max_iter = 2)) + +}) diff --git a/tests/testthat/test_as_bssm.R b/tests/testthat/test_as_bssm.R index a59ba022..475c2ba8 100644 --- a/tests/testthat/test_as_bssm.R +++ b/tests/testthat/test_as_bssm.R @@ -1,32 +1,37 @@ context("Test as_bssm") -test_that("Test conversion from SSModel to ssm_ulg",{ +test_that("Test conversion from SSModel to ssm_ulg", { library(KFAS) model_KFAS <- SSModel(1:10 ~ SSMtrend(2, Q = list(2, 2), P1 = diag(2e3, 2)), H = 2) - expect_error(model_bssm <- ssm_ulg(y = ts(1:10), Z = matrix(c(1, 0), 2, 1), H = sqrt(2), - T = array(c(1, 0, 1, 1), c(2, 2, 1)), R = array(diag(sqrt(2), 2), c(2, 2, 1)), - a1 = matrix(0, 2, 1), P1 = diag(2e3, 2), init_theta = c(0,0)), NA) - expect_error(conv_model_bssm <- as_bssm(model_KFAS, init_theta = c(0, 0)), NA) + expect_error(model_bssm <- ssm_ulg(y = ts(1:10), Z = matrix(c(1, 0), 2, 1), + H = sqrt(2), + T = array(c(1, 0, 1, 1), c(2, 2, 1)), + R = array(diag(sqrt(2), 2), c(2, 2, 1)), + a1 = matrix(0, 2, 1), P1 = diag(2e3, 2), init_theta = c(0, 0)), NA) + expect_error(conv_model_bssm <- as_bssm(model_KFAS, + init_theta = c(0, 0)), NA) expect_equivalent(model_bssm, conv_model_bssm) expect_equivalent(logLik(conv_model_bssm), logLik(model_KFAS)) }) -test_that("Test conversion from SSModel to ssm_ung",{ +test_that("Test conversion from SSModel to ssm_ung", { library(KFAS) model_KFAS <- SSModel(1:10 ~ SSMtrend(2, Q = list(2, 2), P1 = diag(2e3, 2)), u = 2, distribution = "negative binomial") - expect_error(model_bssm <- ssm_ung(y = ts(1:10), Z = matrix(c(1, 0), 2, 1), phi = 2, - T = array(c(1, 0, 1, 1), c(2, 2, 1)), R = array(diag(sqrt(2), 2), c(2, 2, 1)), - a1 = matrix(0, 2, 1), P1 = diag(2e3, 2), distribution = "negative binomial", - state_names = c("level", "slope"), init_theta = c(0,0)), NA) - expect_error(conv_model_bssm <- as_bssm(model_KFAS, init_theta = c(0,0)), NA) + expect_error(model_bssm <- ssm_ung(y = ts(1:10), Z = matrix(c(1, 0), 2, 1), + phi = 2, T = array(c(1, 0, 1, 1), c(2, 2, 1)), + R = array(diag(sqrt(2), 2), c(2, 2, 1)), + a1 = matrix(0, 2, 1), P1 = diag(2e3, 2), + distribution = "negative binomial", + state_names = c("level", "slope"), init_theta = c(0, 0)), NA) + expect_error(conv_model_bssm <- as_bssm(model_KFAS, init_theta = c(0, 0)), NA) expect_equivalent(model_bssm, conv_model_bssm) }) -test_that("Test conversion from SSModel to ssm_mng",{ +test_that("Test conversion from SSModel to ssm_mng", { library(KFAS) set.seed(1) y <- matrix(rbinom(20, size = 10, prob = plogis(rnorm(20, sd = 0.2))), 10, 2) @@ -35,10 +40,35 @@ test_that("Test conversion from SSModel to ssm_mng",{ expect_error(model_bssm <- ssm_mng(y, Z = diag(2), u = 10, T = diag(2), R = array(diag(0.2, 2), c(2, 2, 1)), a1 = matrix(0, 2, 1), P1 = diag(2), distribution = "binomial", - init_theta = c(0,0)), NA) + init_theta = c(0, 0)), NA) # to make the attributes match model_bssm$u <- as.ts(model_bssm$u) model_bssm$initial_mode <- as.ts(model_bssm$initial_mode) - expect_error(conv_model_bssm <- as_bssm(model_KFAS, init_theta = c(0,0)), NA) + expect_error(conv_model_bssm <- as_bssm(model_KFAS, init_theta = c(0, 0)), NA) expect_equivalent(model_bssm, conv_model_bssm) }) + +test_that("Test that time-varying parameters fail", { + library(KFAS) + model_KFAS <- SSModel(1:10 ~ 1, u = 1:10, distribution = "negative binomial") + expect_error(as_bssm(model_KFAS)) + + model_KFAS <- SSModel(1:10 ~ 1, u = 1:10, distribution = "gamma") + expect_error(as_bssm(model_KFAS)) + + model_KFAS <- SSModel(cbind(1:10, 1:10) ~ 1, u = matrix(1:20, 10, 2), + distribution = "negative binomial") + expect_error(as_bssm(model_KFAS)) + + model_KFAS <- SSModel(cbind(1:10, 1:10) ~ 1, u = matrix(1:20,10,2), + distribution = "gamma") + expect_error(as_bssm(model_KFAS)) + + model_KFAS <- SSModel(cbind(1:10, 1:10) ~ 1, u = cbind(1, 1:10), + distribution = c("gamma", "gaussian")) + expect_error(as_bssm(model_KFAS)) + + model_KFAS <- SSModel(cbind(1:10, 1:10) ~ 1, u = matrix(1:20,10,2), + distribution = c("binomial", "poisson")) + expect_error(as_bssm(model_KFAS), NA) +}) diff --git a/tests/testthat/test_as_data_frame.R b/tests/testthat/test_as_data_frame.R new file mode 100644 index 00000000..524bdfc1 --- /dev/null +++ b/tests/testthat/test_as_data_frame.R @@ -0,0 +1,48 @@ +context("Test as.data.frame") + +set.seed(123) +model_bssm <- bsm_lg(rnorm(10, 3), P1 = diag(2, 2), sd_slope = 0, + sd_y = uniform(1, 0, 10), + sd_level = uniform(1, 0, 10)) + +test_that("expanded and not expanded data frame work equally for theta", { + + expect_error(mcmc_bsm <- run_mcmc(model_bssm, iter = 50, seed = 1), NA) + d <- expect_error(as.data.frame(mcmc_bsm), NA) + expect_equal(colnames(d), c("iter", "value", "variable", "weight")) + expect_equal(unique(d$variable), c("sd_y", "sd_level")) + expect_equal(mean(d$value[d$variable == "sd_level"]), + weighted.mean(mcmc_bsm$theta[, 2], mcmc_bsm$counts)) + + d <- expect_error(as.data.frame(mcmc_bsm, expand = FALSE), NA) + expect_equal(colnames(d), c("iter", "value", "variable", "weight")) + expect_equal(unique(d$variable), c("sd_y", "sd_level")) + expect_equal(weighted.mean(d$value[d$variable == "sd_level"], + d$weight[d$variable == "sd_level"]), + weighted.mean(mcmc_bsm$theta[, 2], mcmc_bsm$counts)) +}) + +test_that("expanded and not expanded data frame work equally for states", { + + expect_error(mcmc_bsm <- run_mcmc(model_bssm, iter = 50, seed = 1), NA) + d <- expect_error(as.data.frame(mcmc_bsm, variable = "state"), NA) + expect_equal(colnames(d), c("iter", "value", "variable", "time", "weight")) + expect_equal(unique(d$variable), c("level", "slope")) + expect_equal(mean(d$value[d$variable == "slope" & d$time == 3]), + weighted.mean(mcmc_bsm$alpha[3, 2, ], mcmc_bsm$counts)) + + expect_error(d <- as.data.frame(mcmc_bsm, variable = "state", + expand = FALSE), NA) + expect_equal(colnames(d), c("iter", "value", "variable", "time", "weight")) + expect_equal(unique(d$variable), c("level", "slope")) + expect_equal(weighted.mean(d$value[d$variable == "slope" & d$time == 3], + d$weight[d$variable == "slope" & d$time == 3]), + weighted.mean(mcmc_bsm$alpha[3, 2, ], mcmc_bsm$counts)) + + expect_error(d <- as.data.frame(mcmc_bsm, variable = "theta"), NA) + expect_error(sumr <- summary(mcmc_bsm, variable = "both", return_se = TRUE), + NA) + expect_equal(mean(d$value[d$variable == "sd_y"]), + sumr$theta[2, 2]) + +}) diff --git a/tests/testthat/test_basics.R b/tests/testthat/test_basics.R index add7bf88..f0f5778f 100644 --- a/tests/testthat/test_basics.R +++ b/tests/testthat/test_basics.R @@ -1,25 +1,33 @@ context("Test basics") -test_that("results for Gaussian models are comparable to KFAS",{ +#' @srrstats {G5.4, G5.4b, G5.6, G5.6a, G5.6b, G5.7} Compare with KFAS. + +tol <- 1e-6 + +test_that("results for Gaussian models are comparable to KFAS", { library("KFAS") model_KFAS <- SSModel(1:10 ~ SSMtrend(2, Q = list(0.01^2, 0)), H = 2) model_KFAS$P1inf[] <- 0 diag(model_KFAS$P1) <- 1e2 - - model_bssm <- bsm_lg(1:10, P1 = diag(1e2,2), sd_slope = 0, + + expect_error(bsm_lg(1:10, P1 = diag(1e2, 2), sd_slope = 0, + sd_level = 0.01)) + expect_error(bsm_lg(1:10, P1 = diag(1e2, 2), sd_slope = 0, + sd_y = 0.01)) + model_bssm <- bsm_lg(1:10, P1 = diag(1e2, 2), sd_slope = 0, sd_level = 0.01, sd_y = sqrt(2)) - - expect_equal(logLik(model_KFAS,convtol = 1e-12), logLik(model_bssm,0)) + + expect_equal(logLik(model_KFAS, convtol = 1e-12), logLik(model_bssm, 0)) out_KFAS <- KFS(model_KFAS, filtering = "state", convtol = 1e-12) expect_error(out_bssm <- kfilter(model_bssm), NA) - expect_equivalent(out_KFAS$a, out_bssm$at) - expect_equivalent(out_KFAS$P, out_bssm$Pt) + expect_equivalent(out_KFAS$a, out_bssm$at, tolerance = tol) + expect_equivalent(out_KFAS$P, out_bssm$Pt, tolerance = tol) expect_error(out_bssm <- smoother(model_bssm), NA) - expect_equivalent(out_KFAS$alphahat, out_bssm$alphahat) - expect_equivalent(out_KFAS$V, out_bssm$Vt) + expect_equivalent(out_KFAS$alphahat, out_bssm$alphahat, tolerance = tol) + expect_equivalent(out_KFAS$V, out_bssm$Vt, tolerance = tol) }) -test_that("results for multivariate Gaussian model are comparable to KFAS",{ +test_that("results for multivariate Gaussian model are comparable to KFAS", { library("KFAS") # From the help page of ?KFAS data("Seatbelts", package = "datasets") @@ -30,155 +38,172 @@ test_that("results for multivariate Gaussian model are comparable to KFAS",{ Q = matrix(1), P1inf = diag(2)) + SSMseasonal(period = 12, sea.type = "trigonometric"), data = Seatbelts, H = matrix(NA, 2, 2)) - + diag(kfas_model$P1) <- 50 diag(kfas_model$P1inf) <- 0 - kfas_model$H <- structure(c(0.00544500509177812, 0.00437558178720609, 0.00437558178720609, - 0.00885692410165593), .Dim = c(2L, 2L, 1L)) - kfas_model$R <- structure(c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0152150188066314, 0.0144897116711475 - ), .Dim = c(29L, 1L, 1L), .Dimnames = list(c("log(PetrolPrice).front", - "log(kms).front", "log(PetrolPrice).rear", "log(kms).rear", "law.front", - "sea_trig1.front", "sea_trig*1.front", "sea_trig2.front", "sea_trig*2.front", - "sea_trig3.front", "sea_trig*3.front", "sea_trig4.front", "sea_trig*4.front", - "sea_trig5.front", "sea_trig*5.front", "sea_trig6.front", "sea_trig1.rear", - "sea_trig*1.rear", "sea_trig2.rear", "sea_trig*2.rear", "sea_trig3.rear", - "sea_trig*3.rear", "sea_trig4.rear", "sea_trig*4.rear", "sea_trig5.rear", - "sea_trig*5.rear", "sea_trig6.rear", "custom1", "custom2"), NULL, + kfas_model$H <- structure(c(0.00544500509177812, 0.00437558178720609, + 0.00437558178720609, 0.00885692410165593), .Dim = c(2L, 2L, 1L)) + kfas_model$R <- structure(c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0152150188066314, 0.0144897116711475 + ), .Dim = c(29L, 1L, 1L), .Dimnames = list(c("log(PetrolPrice).front", + "log(kms).front", "log(PetrolPrice).rear", "log(kms).rear", "law.front", + "sea_trig1.front", "sea_trig*1.front", "sea_trig2.front", + "sea_trig*2.front", "sea_trig3.front", "sea_trig*3.front", + "sea_trig4.front", "sea_trig*4.front", + "sea_trig5.front", "sea_trig*5.front", "sea_trig6.front", "sea_trig1.rear", + "sea_trig*1.rear", "sea_trig2.rear", "sea_trig*2.rear", "sea_trig3.rear", + "sea_trig*3.rear", "sea_trig4.rear", "sea_trig*4.rear", "sea_trig5.rear", + "sea_trig*5.rear", "sea_trig6.rear", "custom1", "custom2"), NULL, NULL)) - + bssm_model <- as_bssm(kfas_model) - expect_equivalent(logLik(kfas_model),logLik(bssm_model)) - expect_equivalent(KFS(kfas_model)$alphahat, smoother(bssm_model)$alphahat) - + expect_equivalent(logLik(kfas_model), logLik(bssm_model), tolerance = tol) + expect_equivalent(KFS(kfas_model)$alphahat, + smoother(bssm_model)$alphahat, tolerance = tol) + }) -test_that("different smoothers give identical results",{ - model_bssm <- bsm_lg(log10(AirPassengers), P1 = diag(1e2,13), sd_slope = 0, - sd_y = uniform(0.005, 0, 10), sd_level = uniform(0.01, 0, 10), +test_that("different smoothers give identical results", { + model_bssm <- bsm_lg(log10(AirPassengers), P1 = diag(1e2, 13), sd_slope = 0, + sd_y = uniform(0.005, 0, 10), sd_level = uniform(0.01, 0, 10), sd_seasonal = uniform(0.005, 0, 1)) - + expect_error(out_bssm1 <- smoother(model_bssm), NA) expect_error(out_bssm2 <- fast_smoother(model_bssm), NA) - expect_equivalent(out_bssm2, out_bssm1$alphahat) + expect_equivalent(out_bssm2, out_bssm1$alphahat, tolerance = tol) }) -test_that("results for Poisson model are comparable to KFAS",{ +test_that("results for Poisson model are comparable to KFAS", { library("KFAS") set.seed(1) - model_KFAS <- SSModel(rpois(10, exp(0.2) * (2:11)) ~ SSMtrend(2, Q = list(0.01^2, 0)), + model_KFAS <- SSModel(rpois(10, exp(0.2) * (2:11)) ~ + SSMtrend(2, Q = list(0.01^2, 0)), distribution = "poisson", u = 2:11) model_KFAS$P1inf[] <- 0 diag(model_KFAS$P1) <- 1e2 - - model_bssm <- bsm_ng(model_KFAS$y, P1 = diag(1e2,2), sd_slope = 0, + + model_bssm <- bsm_ng(model_KFAS$y, P1 = diag(1e2, 2), sd_slope = 0, sd_level = 0.01, u = 2:11, distribution = "poisson") - + expect_equal(logLik(model_KFAS), logLik(model_bssm, 0)) out_KFAS <- KFS(model_KFAS, filtering = "state") expect_error(out_bssm <- kfilter(model_bssm), NA) - expect_equivalent(out_KFAS$a, out_bssm$at) - expect_equivalent(out_KFAS$P, out_bssm$Pt) + expect_equivalent(out_KFAS$a, out_bssm$at, tolerance = tol) + expect_equivalent(out_KFAS$P, out_bssm$Pt, tolerance = tol) expect_error(out_bssm <- smoother(model_bssm), NA) - expect_equivalent(out_KFAS$alphahat, out_bssm$alphahat) - expect_equivalent(out_KFAS$V, out_bssm$Vt) + expect_equivalent(out_KFAS$alphahat, out_bssm$alphahat, tolerance = tol) + expect_equivalent(out_KFAS$V, out_bssm$Vt, tolerance = tol) }) test_that("results for binomial model are comparable to KFAS", { library("KFAS") set.seed(1) - model_KFAS <- SSModel(rbinom(10, 2:11, 0.4) ~ SSMtrend(2, Q = list(0.01^2, 0)), + model_KFAS <- SSModel(rbinom(10, 2:11, 0.4) ~ + SSMtrend(2, Q = list(0.01^2, 0)), distribution = "binomial", u = 2:11) model_KFAS$P1inf[] <- 0 diag(model_KFAS$P1) <- 1e2 - - model_bssm <- bsm_ng(model_KFAS$y, P1 = diag(1e2,2), sd_slope = 0, + + model_bssm <- bsm_ng(model_KFAS$y, P1 = diag(1e2, 2), sd_slope = 0, sd_level = 0.01, u = 2:11, distribution = "binomial") - + expect_equal(logLik(model_KFAS), logLik(model_bssm, 0)) out_KFAS <- KFS(model_KFAS, filtering = "state") expect_error(out_bssm <- kfilter(model_bssm), NA) - expect_equivalent(out_KFAS$a, out_bssm$at) - expect_equivalent(out_KFAS$P, out_bssm$Pt) + expect_equivalent(out_KFAS$a, out_bssm$at, tolerance = tol) + expect_equivalent(out_KFAS$P, out_bssm$Pt, tolerance = tol) expect_error(out_bssm <- smoother(model_bssm), NA) - expect_equivalent(out_KFAS$alphahat, out_bssm$alphahat) - expect_equivalent(out_KFAS$V, out_bssm$Vt) + expect_equivalent(out_KFAS$alphahat, out_bssm$alphahat, tolerance = tol) + expect_equivalent(out_KFAS$V, out_bssm$Vt, tolerance = tol) }) -test_that("results for multivariate non-Gaussian model are comparable to KFAS", { +test_that("results for bivariate non-Gaussian model are comparable to KFAS", { library("KFAS") set.seed(1) n <- 10 x1 <- cumsum(rnorm(n)) x2 <- cumsum(rnorm(n, sd = 0.2)) u <- rep(c(1, 15), c(4, 6)) - y <- cbind(rbinom(n, size = u, prob = plogis(x1)), + y <- cbind(rbinom(n, size = u, prob = plogis(x1)), rpois(n, u * exp(x1 + x2)), rgamma(n, 10, 10 / exp(x2)), rnorm(n, x2, 0.1)) - - model_KFAS <- SSModel(y ~ - SSMtrend(1, Q = 1, a1 = -0.5, P1 = 0.5, type = "common", index = 1:2) + + + model_KFAS <- SSModel(y ~ + SSMtrend(1, Q = 1, a1 = -0.5, P1 = 0.5, type = "common", index = 1:2) + SSMtrend(1, Q = 0.2^2, P1 = 1, type = "common", index = 2:4), - distribution = c("binomial", "poisson", "gamma", "gaussian"), + distribution = c("binomial", "poisson", "gamma", "gaussian"), u = cbind(u, u, 10, 0.1^2)) model_bssm <- as_bssm(model_KFAS) - + approx_bssm <- gaussian_approx(model_bssm, conv_tol = 1e-16) approx_KFAS <- approxSSM(model_KFAS, tol = 1e-16) - - expect_equivalent(approx_bssm$y, approx_KFAS$y, tol = 1e-8) - expect_equivalent(approx_bssm$H^2, approx_KFAS$H, tol = 1e-8) - - expect_equivalent(logLik(model_KFAS, nsim = 0), - logLik(model_bssm, particles = 0), tol = 1e-8) - expect_equivalent(logLik(model_KFAS, nsim = 100, seed = 1), - logLik(model_bssm, particles = 100, method = "spdk", seed = 1), tolerance = 1) - + + expect_equivalent(approx_bssm$y, approx_KFAS$y, tolerance = tol) + expect_equivalent(approx_bssm$H^2, approx_KFAS$H, tolerance = tol) + + expect_equivalent(logLik(model_KFAS, nsim = 0), + logLik(model_bssm, particles = 0), tolerance = tol) + expect_equivalent(logLik(model_KFAS, nsim = 100, seed = 1), + logLik(model_bssm, particles = 100, method = "spdk", seed = 1), + tolerance = 1) + expect_equivalent( logLik(model_bssm, particles = 100, method = "psi", seed = 1), - logLik(model_bssm, particles = 100, method = "spdk", seed = 1), tolerance = 1) - + logLik(model_bssm, particles = 100, method = "spdk", seed = 1), + tolerance = 1) + # note large tolerance due to the sd of bsf expect_equivalent( logLik(model_bssm, particles = 100, method = "psi", seed = 1), - logLik(model_bssm, particles = 100, method = "bsf", seed = 1), tolerance = 10) - + logLik(model_bssm, particles = 100, method = "bsf", seed = 1), + tolerance = 10) + out_KFAS <- KFS(model_KFAS) expect_error(out_bssm <- smoother(model_bssm), NA) - expect_equivalent(out_KFAS$alphahat, out_bssm$alphahat, tolerance = 1e-8) - expect_equivalent(out_KFAS$V, out_bssm$Vt, tolerance = 1e-8) + expect_equivalent(out_KFAS$alphahat, out_bssm$alphahat, tolerance = tol) + expect_equivalent(out_KFAS$V, out_bssm$Vt, tolerance = tol) is_KFAS <- importanceSSM(model_KFAS, nsim = 1e4) expect_error(is_bssm <- importance_sample(model_bssm, nsim = 1e4), NA) - expect_equivalent(apply(is_bssm$alpha, 1:2, mean)[1:n,], + expect_equivalent(apply(is_bssm$alpha, 1:2, mean)[1:n, ], apply(is_KFAS$samples, 1:2, mean), tolerance = 0.1) - expect_equivalent(apply(is_bssm$alpha, 1:2, sd)[1:n,], + expect_equivalent(apply(is_bssm$alpha, 1:2, sd)[1:n, ], apply(is_KFAS$samples, 1:2, sd), tolerance = 0.1) }) test_that("multivariate normal pdf works", { - - expect_equivalent(bssm:::dmvnorm(1, 3, matrix(2, 1, 1), TRUE, TRUE), - dnorm(1, 3, 2, log = TRUE)) - expect_equivalent(bssm:::dmvnorm(1, 3, matrix(4, 1, 1), TRUE, TRUE), - dnorm(1, 3, 4, log = TRUE)) - + + expect_equivalent(bssm:::dmvnorm(1, 3, matrix(2, 1, 1), TRUE, TRUE), + dnorm(1, 3, 2, log = TRUE), tolerance = tol) + expect_equivalent(bssm:::dmvnorm(1, 3, matrix(4, 1, 1), TRUE, TRUE), + dnorm(1, 3, 4, log = TRUE), tolerance = tol) + set.seed(1) a <- crossprod(matrix(rnorm(9), 3, 3)) logp1 <- expect_error(bssm:::dmvnorm(1:3, -0.1 * (3:1), a, FALSE, TRUE), NA) expect_equivalent(logp1, -14.0607446337904, tolerance = 1e-6) - + chola <- t(chol(a)) - logp2 <- expect_error(bssm:::dmvnorm(1:3, -0.1 * (3:1), chola, TRUE, TRUE), NA) - expect_equivalent(logp2, logp1, tolerance = 1e-8) - + logp2 <- expect_error(bssm:::dmvnorm(1:3, -0.1 * (3:1), chola, TRUE, TRUE), + NA) + expect_equivalent(logp2, logp1, tolerance = tol) + b <- matrix(0, 3, 3) constant <- bssm:::precompute_dmvnorm(a, b, 0:2) - expect_equivalent(logp1, bssm:::fast_dmvnorm(1:3, -0.1*(3:1), b, 0:2, constant), tolerance = 1e-8) - - a[2,] <- a[, 2] <- 0 + expect_equivalent(logp1, + bssm:::fast_dmvnorm(1:3, -0.1 * (3:1), b, 0:2, constant), tolerance = 1e-8) + + a[2, ] <- a[, 2] <- 0 logp3 <- expect_error(bssm:::dmvnorm(1:3, -0.1 * (3:1), a, FALSE, TRUE), NA) expect_equivalent(logp3, -12.5587625856078, tolerance = 1e-6) -}) \ No newline at end of file +}) + +test_that("asymptotic_var fails with improper weights", { + x <- rnorm(10) + expect_error(asymptotic_var(x, 0)) + expect_error(asymptotic_var(x, rep(0, length(x)))) + expect_error(asymptotic_var(x, c(-1, runif(9)))) + expect_error(asymptotic_var(x, c(Inf, runif(9)))) +}) diff --git a/tests/testthat/test_bootstrap_filter.R b/tests/testthat/test_bootstrap_filter.R index bc00920b..3d52d048 100644 --- a/tests/testthat/test_bootstrap_filter.R +++ b/tests/testthat/test_bootstrap_filter.R @@ -2,11 +2,14 @@ context("Test that bootstrap_filter works") -test_that("Test that bsm_lg gives identical results with ssm_ulg",{ - expect_error(model_ssm_ulg <- ssm_ulg(y = 1:10, Z = matrix(c(1, 0), 2, 1), H = 2, - T = array(c(1, 0, 1, 1), c(2, 2, 1)), R = array(diag(2, 2), c(2, 2, 1)), - a1 = matrix(0, 2, 1), P1 = diag(2, 2), state_names = c("level", "slope")), NA) - expect_error(bsf_ssm_ulg <- bootstrap_filter(model_ssm_ulg, 10, seed = 1), NA) +test_that("Test that bsm_lg gives identical results with ssm_ulg", { + expect_error(model_ssm_ulg <- ssm_ulg(y = 1:10, Z = matrix(c(1, 0), 2, 1), + H = 2, T = array(c(1, 0, 1, 1), c(2, 2, 1)), + R = array(diag(2, 2), c(2, 2, 1)), + a1 = matrix(0, 2, 1), P1 = diag(2, 2), state_names = c("level", "slope")), + NA) + expect_error(bsf_ssm_ulg <- bootstrap_filter(model_ssm_ulg, 10, seed = 1), + NA) expect_error(model_bsm <- bsm_lg(1:10, sd_level = 2, sd_slope = 2, sd_y = 2, P1 = diag(2, 2)), NA) expect_error(bsf_bsm <- bootstrap_filter(model_bsm, 10, seed = 1), NA) @@ -15,23 +18,37 @@ test_that("Test that bsm_lg gives identical results with ssm_ulg",{ tol <- 1e-8 -test_that("Test that gaussian bsf still works",{ +test_that("Test that linear-gaussian bsf still works", { - expect_error(model_ssm_ulg <- ssm_ulg(y = 1:10, Z = matrix(c(1, 0), 2, 1), H = 2, - T = array(c(1, 0, 1, 1), c(2, 2, 1)), R = array(diag(2, 2), c(2, 2, 1)), - a1 = matrix(0, 2, 1), P1 = diag(2, 2), state_names = c("level", "slope")), NA) - expect_error(bsf_ssm_ulg <- bootstrap_filter(model_ssm_ulg, 10, seed = 1), NA) + expect_error(model_ssm_ulg <- ssm_ulg(y = 1:10, Z = matrix(c(1, 0), 2, 1), + H = 2, T = array(c(1, 0, 1, 1), c(2, 2, 1)), + R = array(diag(2, 2), c(2, 2, 1)), + a1 = matrix(0, 2, 1), P1 = diag(2, 2), state_names = c("level", "slope")), + NA) + expect_error(bsf_ssm_ulg <- bootstrap_filter(model_ssm_ulg, 10, seed = 1), + NA) expect_gte(min(bsf_ssm_ulg$weights), 0) expect_lt(max(bsf_ssm_ulg$weights), Inf) expect_true(is.finite(bsf_ssm_ulg$logLik)) expect_true(is.finite(sum(bsf_ssm_ulg$att))) expect_true(is.finite(sum(bsf_ssm_ulg$Ptt))) + + expect_error(model_ar1_lg <- ar1_lg(y = 1:10, + rho = tnormal(0.6, 0, 0.5, -1, 1), + sigma = gamma(1,2,2), sd_y = 0.1, mu = 1), NA) + expect_error(bsf_ar1_lg <- bootstrap_filter(model_ar1_lg, 10, seed = 1), + NA) + expect_gte(min(bsf_ar1_lg$weights), 0) + expect_lt(max(bsf_ar1_lg$weights), Inf) + expect_true(is.finite(bsf_ar1_lg$logLik)) + expect_true(is.finite(sum(bsf_ar1_lg$att))) + expect_true(is.finite(sum(bsf_ar1_lg$Ptt))) }) -test_that("Test that poisson bsm_ng still works",{ +test_that("Test that poisson bsm_ng still works", { - expect_error(model <- bsm_ng(1:10, sd_level = 2, sd_slope = 2, P1 = diag(2, 2), - distribution = "poisson"), NA) + expect_error(model <- bsm_ng(1:10, sd_level = 2, sd_slope = 2, + P1 = diag(2, 2), distribution = "poisson"), NA) expect_error(bsf_poisson <- bootstrap_filter(model, 10, seed = 1), NA) expect_gte(min(bsf_poisson$weights), 0) @@ -41,9 +58,12 @@ test_that("Test that poisson bsm_ng still works",{ expect_true(is.finite(sum(bsf_poisson$Ptt))) }) -test_that("Test that binomial bsm_ng still works",{ +test_that("Test that binomial ar1_ng still works", { - expect_error(model <- bsm_ng(c(1,0,1,1,1,0,0,0), sd_level = 2, sd_slope = 2, P1 = diag(2, 2), + expect_error(model <- ar1_ng(c(1, 0, 1, 1, 1, 0, 0, 0), + rho = uniform(0.9, 0, 1), sigma = gamma(1, 2, 2), + mu = normal(1, 0, 1), + xreg = 1:8, beta = normal(0, 0, 0.1), distribution = "binomial"), NA) expect_error(bsf_binomial <- bootstrap_filter(model, 10, seed = 1), NA) @@ -56,10 +76,10 @@ test_that("Test that binomial bsm_ng still works",{ }) - -test_that("Test that negative binomial bsm_ng still works",{ +test_that("Test that negative binomial bsm_ng still works", { - expect_error(model <- bsm_ng(c(1,0,1,1,1,0,0,0), sd_level = 2, sd_slope = 2, P1 = diag(2, 2), + expect_error(model <- bsm_ng(c(1, 0, 1, 1, 1, 0, 0, 0), sd_level = 2, + sd_slope = 2, P1 = diag(2, 2), distribution = "negative binomial", phi = 0.1, u = 2), NA) expect_error(bsf_nbinomial <- bootstrap_filter(model, 10, seed = 1), NA) @@ -71,9 +91,9 @@ test_that("Test that negative binomial bsm_ng still works",{ }) -test_that("Test that still svm works",{ +test_that("Test that still svm works", { data("exchange") - model <- svm(exchange, rho = uniform(0.98,-0.999,0.999), + model <- svm(exchange, rho = uniform(0.98, -0.999, 0.999), sd_ar = halfnormal(0.2, 5), sigma = halfnormal(1, 2)) expect_error(bsf_svm <- bootstrap_filter(model, 10, seed = 1), NA) diff --git a/tests/testthat/test_ekpf.R b/tests/testthat/test_ekpf.R new file mode 100644 index 00000000..fe5e00c2 --- /dev/null +++ b/tests/testthat/test_ekpf.R @@ -0,0 +1,85 @@ +context("Test EKF") + + +test_that("Particle filtering based on EKF works", { + + skip_on_cran() + set.seed(1) + n <- 10 + x <- y <- numeric(n) + y[1] <- rnorm(1, exp(x[1]), 0.1) + for(i in 1:(n-1)) { + x[i+1] <- rnorm(1, sin(x[i]), 0.1) + y[i+1] <- rnorm(1, exp(x[i+1]), 0.1) + } + + pntrs <- cpp_example_model("nlg_sin_exp") + + expect_error(model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(log_H = log(0.1), log_R = log(0.1)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state"), NA) + + expect_error(out <- ekpf_filter(model_nlg, 100), NA) + expect_lt(out$logLik, 6) + expect_gt(out$logLik, 1) + expect_gte(min(out$w), 0-1e16) + expect_lte(max(out$w), 1+1e16) + expect_warning(ekpf_filter(model_nlg, nsim = 10)) + + out_ekf <- particle_smoother(model_nlg, 1000, method = "ekf") + out_psi <- particle_smoother(model_nlg, 1000, method = "psi") + out_bsf <- particle_smoother(model_nlg, 1000, method = "bsf") + expect_equal(out_ekf$alphahat[9:10], + c(0.0263875638744833, 0.0734903567971838), tol = 0.1) + expect_equal(out_psi$alphahat[9:10], + c(0.0263875638744833, 0.0734903567971838), tol = 0.1) + expect_equal(out_bsf$alphahat[9:10], + c(0.0263875638744833, 0.0734903567971838), tol = 0.1) + +}) + +test_that("EKF and IEKF work", { + skip_on_cran() + set.seed(1) + n <- 10 + x <- y <- numeric(n) + y[1] <- rnorm(1, exp(x[1]), 0.1) + for(i in 1:(n-1)) { + x[i+1] <- rnorm(1, sin(x[i]), 0.1) + y[i+1] <- rnorm(1, exp(x[i+1]), 0.1) + } + y[2:3] <- NA + pntrs <- cpp_example_model("nlg_sin_exp") + + expect_error(model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(log_H = log(0.1), log_R = log(0.1)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state"), NA) + + expect_equal(ekf(model_nlg)$logLik, 2.65163101109689) + expect_equal(ekf(model_nlg, iekf_iter = 2)$logLik, + logLik(model_nlg, method = "ekf", iekf_iter = 2, particles = 0)) + expect_equal(ekf(model_nlg, iekf_iter = 1)$logLik, 2.61650080342709) + expect_equal(ekf(model_nlg, iekf_iter = 1), + ekf(model_nlg, iekf_iter = 2)) + + out_ekf1 <- ekf_smoother(model_nlg) + out_ekf2 <- ekf_fast_smoother(model_nlg) + expect_equal(out_ekf1$alphahat[9:10], + c(0.0333634309012196, 0.0797729159367873), tol = 0.1) + expect_equal(out_ekf1$alphahat, out_ekf2) + expect_equal( + ekf_fast_smoother(model_nlg, iekf_iter = 2), + ekf_smoother(model_nlg, iekf_iter = 2)$alphahat) + + expect_error(ukf(model_nlg), NA) + expect_error(ukf(model_nlg, alpha = -1)) + expect_error(ukf(model_nlg, beta = -1)) + expect_error(ukf(model_nlg, kappa = -1)) + expect_error(bootstrap_filter(model_nlg, 10), NA) +}) diff --git a/tests/testthat/test_is.R b/tests/testthat/test_is.R index daf74318..033792f2 100644 --- a/tests/testthat/test_is.R +++ b/tests/testthat/test_is.R @@ -1,21 +1,59 @@ context("Test importance_sample") -test_that("Test that poisson bsm_ng give identical results with ssm_ung",{ +test_that("Test that bssm recovers the parameters of the Seatbelts model", { + + #' @srrstats {G5.6, G5.6a, G5.6b, G5.7, G5.9b} Replicate Durbin&Koopman (1997) + + model <- bsm_ng(Seatbelts[, "VanKilled"], distribution = "poisson", + sd_level = 1, sd_seasonal = 1, xreg = Seatbelts[, "law"], + beta = normal(0, 0, 1)) + + obj <- function(theta) { + model$beta[1] <- theta[1] + model$R[1, 1, 1] <- theta[2] + model$R[2, 2, 1] <- theta[3] + -logLik(model, particles = 0) + } + + fit <- optim(c(0, 0, 0), obj, method = "L-BFGS-B", + lower = c(-Inf, 0, 0), upper = c(10, 10, 10)) + + DK1997 <- c(-0.278, 0.0245, 0) # From Durbin and Koopman (1997) + expect_equal(fit$par, DK1997, tol = 0.01) + + # fixed seed for smooth likelihood optimization (enough only for "spdk") + fixed_seed <- sample(1:1e6, size = 1) + # Same but with importance sampling + obj <- function(theta) { + model$beta[1] <- theta[1] + model$R[1, 1, 1] <- theta[2] + model$R[2, 2, 1] <- theta[3] + -logLik(model, particles = 10, method = "spdk", seed = fixed_seed) + } + + fit_is <- optim(c(0, 0, 0), obj, method = "L-BFGS-B", + lower = c(-Inf, 0, 0), upper = c(10, 10, 10)) + + # essentially identical results in this case + expect_equal(fit_is$par, DK1997, tol = 0.01) +}) + +test_that("Test that poisson bsm_ng give identical results with ssm_ung", { expect_error(model_ssm_ung <- ssm_ung(y = 1:10, Z = matrix(c(1, 0), 2, 1), T = array(c(1, 0, 1, 1), c(2, 2, 1)), R = array(diag(2, 2), c(2, 2, 1)), a1 = matrix(0, 2, 1), P1 = diag(2, 2), state_names = c("level", "slope"), distribution = "poisson"), NA) expect_error(sim_ssm_ung <- importance_sample(model_ssm_ung, 4, seed = 2), NA) - expect_error(model_bsm_ng <- bsm_ng(1:10, sd_level = 2, sd_slope = 2, P1 = diag(2, 2), - distribution = "poisson"), NA) + expect_error(model_bsm_ng <- bsm_ng(1:10, sd_level = 2, sd_slope = 2, + P1 = diag(2, 2), distribution = "poisson"), NA) expect_error(sim_bsm_ng <- importance_sample(model_bsm_ng, 4, seed = 2), NA) expect_equal(sim_bsm_ng, sim_ssm_ung) }) -test_that("Test that svm still works",{ +test_that("Test that svm still works", { data("exchange") - model <- svm(exchange, rho = uniform(0.98,-0.999,0.999), + model <- svm(exchange, rho = uniform(0.98, -0.999, 0.999), sd_ar = halfnormal(0.2, 5), sigma = halfnormal(1, 2)) expect_error(sim <- importance_sample(model, 10, seed = 2), NA) diff --git a/tests/testthat/test_mcmc.R b/tests/testthat/test_mcmc.R index 25ca6cf4..802b090f 100644 --- a/tests/testthat/test_mcmc.R +++ b/tests/testthat/test_mcmc.R @@ -1,72 +1,409 @@ context("Test MCMC") +#' @srrstats {G5.4, G5.4a, G5.4b, G5.4c, G5.5, BS7.0, BS7.1, BS7.2} +#' Replicate Helske & Vihola (2021). + tol <- 1e-8 -test_that("MCMC results for Gaussian model are correct",{ + +test_that("prior and posterior distributions coincide when no data is used", { + + skip_on_cran() + + set.seed(1) + n <- 30 + x <- rnorm(n) + model <- ar1_ng(rep(NA, n), + xreg = x, + distribution = "negative binomial", + rho = uniform_prior(0.9, 0, 1), + sigma = gamma_prior(1, 2, 10), + mu = normal_prior(1, 0.2, 0.5), + phi = gamma_prior(0.4, 2, 1), + beta = normal_prior(0.5, 0, 1)) + + prior_sumr <- as.data.frame(rbind( + rho = c(0.5, sqrt(1/12)), + sigma = c(0.2, sqrt(2)/10), + mu = c(0.2, 0.5), + phi = c(2, sqrt(2)), + beta = c(0, 1))) + + # approx is enough here, the weights are uniform when there is no data + fit <- run_mcmc(model, iter = 2e5,burnin = 1e4, mcmc_type = "approx") + expect_equivalent(prior_sumr, + dplyr::arrange(summary(fit)[, c(2, 3)], order(rownames(prior_sumr))), + tol = 0.1) +}) + + +test_that("MCMC results from bssm paper are still correct", { + skip_on_cran() + + data(negbin_series) + bssm_model <- bsm_ng(negbin_series[, 1], + xreg = negbin_series[, 2], + beta = normal(0, 0, 10), + phi = halfnormal(1, 10), + sd_level = halfnormal(0.1, 1), + sd_slope = halfnormal(0.01, 0.1), + a1 = c(0, 0), P1 = diag(c(10, 0.1)^2), + distribution = "negative binomial") + + # run the MCMC + fit_bssm <- run_mcmc(bssm_model, iter = 6e4, burnin = 1e4, + particles = 10, seed = 1) + expect_error(sumr_theta <- summary(fit_bssm)[, "Mean"], NA) + paper_theta <- c(-0.912, 5.392, 0.092, 0.003) + expect_equivalent(sumr_theta, paper_theta, tol = 0.01) + + expect_error(sumr_alpha <- summary(fit_bssm, + variable = "states", times = 200)$Mean, + NA) + paper_alpha <- c(6.962, 0.006) + expect_equivalent(sumr_alpha, paper_alpha, tol = 0.01) + +}) + +#' @srrstats {BS7.3} +test_that("scaling is linear with respect to the length of the time series", { + skip_on_cran() + + set.seed(1) + n <- 2^14 + + mu <- 2 + rho <- 0.7 + sd_y <- 0.1 + sigma <- 0.5 + beta <- -1 + x <- rnorm(n) + z <- y <- numeric(n) + z[1] <- rnorm(1, mu, sigma / sqrt(1 - rho^2)) + y[1] <- rnorm(1, beta * x[1] + z[1], sd_y) + for(i in 2:n) { + z[i] <- rnorm(1, mu * (1 - rho) + rho * z[i - 1], sigma) + y[i] <- rnorm(1, beta * x[i] + z[i], sd_y) + } + # run the MCMC with various number observations + m <- seq(1000, 10000, by = 3000) + times <- numeric(length(m)) + for(i in seq_along(m)) { + model <- ar1_lg(y[1:m[i]], + xreg = x[1:m[i]], + rho = uniform(0.5, -1, 1), + sigma = halfnormal(1, 10), + mu = normal(0, 0, 1), + sd_y = halfnormal(1, 10), + beta = normal(0, 0, 1)) + + times[i] <- run_mcmc(model, iter = 2e4)$time[3] + } + # standard Kalman filter has complexity of O(n * m) where n is number of time + # points and m is the number of states + expect_equivalent(min(times / m), max(times / m), tol = 0.1) +}) + + +test_that("run_mcmc throws error with improper arguments", { set.seed(123) - model_bssm <- bsm_lg(rnorm(10,3), P1 = diag(2,2), sd_slope = 0, + model_bssm <- bsm_lg(rnorm(10, 3), P1 = diag(2, 2), sd_slope = 0, sd_y = uniform(1, 0, 10), sd_level = uniform(1, 0, 10)) - expect_error(mcmc_bsm <- run_mcmc(model_bssm, iter = 50, seed = 1), NA) + expect_error(mcmc_bsm <- run_mcmc(model_bssm, iter = 50, + end_adaptive_phase = 4), + "Argument 'end_adaptive_phase' should be TRUE or FALSE.") + + out <- run_mcmc(model_bssm, iter = 10, output_type = "theta") + expect_error(summary(out, return_se = 2), + "Argument 'return_se' should be TRUE or FALSE.") + expect_error(summary(out, variable = "both"), + "Cannot return summary of states as the MCMC type was not 'full'.") + + model_bssm$theta[] <- Inf + expect_error(run_mcmc(model_bssm, iter = 1e4, particles = 10), + "Initial prior probability is not finite.") +}) + +#' @srrstats {BS2.13} +test_that("MCMC messages can be suppressed", { + + model_bssm <- bsm_lg(rnorm(10, 3), P1 = diag(2, 2), sd_slope = 0, + sd_y = uniform(1, 0, 10), + sd_level = uniform(1, 0, 10)) + + expect_equal(capture_output(run_mcmc(model_bssm, iter = 100, + verbose = FALSE)), "") + expect_equal(capture_output(run_mcmc(model_bssm, iter = 10, + verbose = FALSE)), "") +}) + +test_that("MCMC results for Gaussian model are correct", { + set.seed(123) + model_bssm <- bsm_lg(rnorm(10, 3), P1 = diag(2, 2), sd_slope = 0, + sd_y = uniform(1, 0, 10), + sd_level = uniform(1, 0, 10)) - expect_equal(run_mcmc(model_bssm, iter = 100, seed = 1)[-14], + expect_error(mcmc_bsm <- run_mcmc(model_bssm, iter = 50, seed = 1), NA) + + expect_equal( + run_mcmc(model_bssm, iter = 100, seed = 1)[-14], run_mcmc(model_bssm, iter = 100, seed = 1)[-14]) - expect_equal(run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "summary")[-15], + expect_equal( + run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "summary")[-15], run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "summary")[-15]) - expect_equal(run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "theta")[-13], + expect_equal( + run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "theta")[-13], run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "theta")[-13]) - expect_equal(run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "theta")$theta, + expect_equal( + run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "theta")$theta, run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "summary")$theta) - expect_equal(run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "theta")$acceptance_rate, - run_mcmc(model_bssm, iter = 100, seed = 1, output_type = "summary")$acceptance_rate) + expect_equal( + run_mcmc(model_bssm, iter = 100, seed = 1, + output_type = "theta")$acceptance_rate, + run_mcmc(model_bssm, iter = 100, seed = 1, + output_type = "summary")$acceptance_rate) expect_gt(mcmc_bsm$acceptance_rate, 0) expect_gte(min(mcmc_bsm$theta), 0) expect_lt(max(mcmc_bsm$theta), Inf) expect_true(is.finite(sum(mcmc_bsm$alpha))) - + + set.seed(1) + n <- 20 + x1 <- rnorm(n) + x2 <- rnorm(n) + b1 <- 1 + cumsum(rnorm(n, sd = 0.5)) + b2 <- 2 + cumsum(rnorm(n, sd = 0.1)) + y <- 1 + b1 * x1 + b2 * x2 + rnorm(n, sd = 0.1) + + Z <- rbind(1, x1, x2) + H <- 0.1 + T <- diag(3) + R <- diag(c(0, 1, 0.1)) + a1 <- rep(0, 3) + P1 <- diag(10, 3) + + # updates the model given the current values of the parameters + update_fn <- function(theta) { + R <- diag(c(0, theta[1], theta[2])) + dim(R) <- c(3, 3, 1) + list(R = R, H = theta[3]) + } + # prior for standard deviations as half-normal(1) + prior_fn <- function(theta) { + if(any(theta < 0)) { + log_p <- -Inf + } else { + log_p <- sum(dnorm(theta, 0, 1, log = TRUE)) + } + log_p + } + + model <- ssm_ulg(y, Z, H, T, R, a1, P1, + init_theta = c(1, 0.1, 0.1), + update_fn = update_fn, prior_fn = prior_fn) + + expect_error(out <- run_mcmc(model, iter = 50, seed = 1), NA) + + expect_gt(out$acceptance_rate, 0) + expect_gte(min(out$theta), 0) + expect_lt(max(out$theta), Inf) + expect_true(is.finite(sum(out$alpha))) + + model2 <- ssm_ulg(y, Z, H, T, R, a1, P1) + expect_error(run_mcmc(model2, iter = 50)) + + expect_equal( + run_mcmc(model, iter = 100, seed = 1)[-14], + run_mcmc(model, iter = 100, seed = 1)[-14]) + expect_equal( + run_mcmc(model, iter = 100, seed = 1, output_type = "summary")[-15], + run_mcmc(model, iter = 100, seed = 1, output_type = "summary")[-15]) + expect_equal( + run_mcmc(model, iter = 100, seed = 1, output_type = "theta")[-13], + run_mcmc(model, iter = 100, seed = 1, output_type = "theta")[-13]) + expect_equal( + run_mcmc(model, iter = 100, seed = 1, output_type = "theta")$theta, + run_mcmc(model, iter = 100, seed = 1, output_type = "summary")$theta) + expect_equal( + run_mcmc(model, iter = 100, seed = 1, + output_type = "theta")$acceptance_rate, + run_mcmc(model, iter = 100, seed = 1, + output_type = "summary")$acceptance_rate) }) +test_that("MCMC for ssm_mng work", { + + set.seed(1) + n <- 20 + x <- cumsum(rnorm(n, sd = 0.5)) + phi <- 2 + y <- cbind(rgamma(n, shape = phi, scale = exp(x) / phi), + rbinom(n, 1, plogis(x))) + + Z <- matrix(1, 2, 1) + T <- 1 + R <- 0.5 + a1 <- 0 + P1 <- 1 + + update_fn <- function(theta) { + list(R = array(theta[1], c(1, 1, 1)), phi = c(theta[2], 1)) + } + + prior_fn <- function(theta) { + ifelse(all(theta > 0), sum(dnorm(theta, 0, 1, log = TRUE)), -Inf) + } + + expect_error(model <- ssm_mng(y, Z, T, R, a1, P1, phi = c(2, 1), + init_theta = c(0.5, 2), + distribution = c("gamma", "binomial"), + update_fn = update_fn, prior_fn = prior_fn), NA) + + expect_error(run_mcmc(model, iter = 50, + local_approx = 4)) + expect_error(run_mcmc(model, iter = 50, + particles = 1)) + + for(type in c("pm", "da", "is1", "is3", "is3", "approx")) { + for(method in c("psi", "bsf", "spdk")) { + for(output in c("full", "summary", "theta")) { + expect_error( + out <- run_mcmc(model, mcmc_type = type, sampling_method = method, + output_type = output, iter = 1000, seed = 1, particles = 10), NA) + expect_equal(sum(is.na(out$theta)), 0) + expect_equal(sum(is.na(out$alpha)), 0) + expect_equal(sum(!is.finite(out$theta)), 0) + expect_equal(sum(!is.finite(out$alpha)), 0) + expect_equal(sum(!is.finite(out$posterior)), 0) + expect_gt(out$acceptance_rate, 0) + expect_gte(min(out$theta), 0) + } + } + } + + expect_error(bootstrap_filter(model, 10), NA) +}) -test_that("DA-MCMC results for Poisson model are correct",{ +test_that("MCMC results with psi-APF for Poisson model are correct", { + set.seed(123) - model_bssm <- bsm_ng(rpois(10, exp(0.2) * (2:11)), P1 = diag(2, 2), sd_slope = 0, - sd_level = uniform(2, 0, 10), u = 2:11, distribution = "poisson") + model_bssm <- bsm_ng(rpois(10, exp(0.2) * (2:11)), P1 = diag(2, 2), + sd_slope = 0, sd_level = uniform(2, 0, 10), u = 2:11, + distribution = "poisson") expect_error(mcmc_poisson <- run_mcmc(model_bssm, mcmc_type = "da", iter = 100, particles = 5, seed = 42), NA) - expect_equal(run_mcmc(model_bssm, mcmc_type = "da", iter = 100, seed = 1, particles = 5)[-14], - run_mcmc(model_bssm, mcmc_type = "da", iter = 100, seed = 1, particles = 5)[-14]) - expect_equal(run_mcmc(model_bssm, mcmc_type = "da", iter = 100, seed = 1, output_type = "summary", particles = 5)[-15], - run_mcmc(model_bssm, mcmc_type = "da", iter = 100, seed = 1, output_type = "summary", particles = 5)[-15]) - expect_equal(run_mcmc(model_bssm, mcmc_type = "da", iter = 100, seed = 1, output_type = "theta", particles = 5)[-13], - run_mcmc(model_bssm, mcmc_type = "da", iter = 100, seed = 1, output_type = "theta", particles = 5)[-13]) - expect_gt(mcmc_poisson$acceptance_rate, 0) expect_gte(min(mcmc_poisson$theta), 0) expect_lt(max(mcmc_poisson$theta), Inf) expect_true(is.finite(sum(mcmc_poisson$alpha))) + sumr <- expect_error(summary(mcmc_poisson, variable = "both"), NA) + + expect_lt(sum(abs(sumr$theta[1, 2:3] - + c(0.25892090511681, 0.186796779799571))), 0.5) + + states <- expand_sample(mcmc_poisson, variable = "states") + + expect_error(expand_sample(mcmc_poisson, variable = "blaablaa")) + expect_error(expand_sample(mcmc_poisson, variable = "states", by_states = 2)) + expect_error(expand_sample(mcmc_poisson, variable = "states", times = 0)) + expect_error(expand_sample(mcmc_poisson, variable = "states", times = 1:100)) + expect_error(expand_sample(mcmc_poisson, variable = "states", states = 0)) + expect_error(expand_sample(mcmc_poisson, variable = "states", states = "a")) + expect_error(expand_sample(mcmc_poisson, variable = "states", + states = list(4))) + + + expect_equal(sumr$states$Mean[sumr$states$variable == "level"], + as.numeric(colMeans(states$level))) + + expect_error(as_draws(mcmc_poisson), NA) + expect_error(d <- as.data.frame(mcmc_poisson, variable = "state"), NA) + x <- dplyr::pull(dplyr::summarise( + dplyr::group_by( + dplyr::filter(d, variable == "level"), time), + mean = mean(value)), mean) + expect_equal(x, as.numeric(colMeans(states$level))) + + for(type in c("pm", "da", "is1", "is3", "is3", "approx")) { + z <- 2*type%in%c("is1", "is3", "is3", "approx") + expect_equal( + run_mcmc(model_bssm, mcmc_type = type, iter = 100, seed = 1, + particles = 5)[-14 - z], + run_mcmc(model_bssm, mcmc_type = type, iter = 100, seed = 1, + particles = 5)[-14 - z]) + expect_equal( + run_mcmc(model_bssm, mcmc_type = type, iter = 100, seed = 1, + output_type = "summary", particles = 5)[-15 - z], + run_mcmc(model_bssm, mcmc_type = type, iter = 100, seed = 1, + output_type = "summary", particles = 5)[-15 - z]) + expect_equal( + run_mcmc(model_bssm, mcmc_type = type, iter = 100, seed = 1, + output_type = "theta", particles = 5)[-13 - z], + run_mcmc(model_bssm, mcmc_type = type, iter = 100, seed = 1, + output_type = "theta", particles = 5)[-13 - z]) + } + + expect_error(expand_sample(run_mcmc(model_bssm, iter = 100, seed = 1, + output_type = "theta", mcmc_type = "approx"), variable = "states")) }) -test_that("MCMC results for SV model using IS-correction are correct",{ + +test_that("MCMC using SPDK for Gamma model works", { + set.seed(123) - expect_error(model_bssm <- svm(rnorm(10), rho = uniform(0.95,-0.999,0.999), + n <- 20 + u <- rgamma(n, 3, 1) + phi <- 5 + x <- cumsum(rnorm(n, 0, 0.5)) + y <- rgamma(n, shape = phi, scale = u * exp(x) / phi) + model_bssm <- bsm_ng(y, + sd_level = gamma(0.1, 2, 10), u = u, phi = gamma(2, 2, 0.1), + distribution = "gamma", P1 = 2) + + expect_error(mcmc_gamma <- run_mcmc(model_bssm, sampling_method = "spdk", + iter = 1000, particles = 5, seed = 42), NA) + + expect_gt(mcmc_gamma$acceptance_rate, 0) + expect_gte(min(mcmc_gamma$theta), 0) + expect_lt(max(mcmc_gamma$theta), Inf) + expect_true(is.finite(sum(mcmc_gamma$alpha))) + + expect_lt(sum(abs(summary(mcmc_gamma)$Mean - + c(12.353642743311, 0.542149368711246))), 2) + +}) + +test_that("MCMC results for SV model using IS-correction are correct", { + set.seed(123) + + expect_error(svm(rnorm(10), rho = uniform(0.95, -0.999, 0.999), + sd_ar = halfnormal(1, 5), mu = 4, sigma = halfnormal(1, 2))) + + expect_error(model_bssm <- svm(rnorm(10), rho = uniform(0.95, -0.999, 0.999), sd_ar = halfnormal(1, 5), sigma = halfnormal(1, 2)), NA) + expect_error(logLik(model_bssm, particles = 0, method = "bsf")) + expect_equal(run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is1", seed = 1)[-16], - run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is1", seed = 1)[-16]) + run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is1", + seed = 1)[-16]) expect_equal(run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is2", seed = 1)[-16], - run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is2", seed = 1)[-16]) + run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is2", + seed = 1)[-16]) expect_equal(run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is3", seed = 1)[-16], - run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is3", seed = 1)[-16]) + run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is3", + seed = 1)[-16]) expect_equal(run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is2", seed = 1, sampling_mcmc_type = "psi")[-16], @@ -78,12 +415,99 @@ test_that("MCMC results for SV model using IS-correction are correct",{ run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is2", seed = 1, sampling_mcmc_type = "bsf")[-16]) + expect_equal(run_mcmc(model_bssm, iter = 100, particles = 10, + mcmc_type = "is2", seed = 1, sampling_mcmc_type = "psi", + threads = 2L)[-16], + run_mcmc(model_bssm, iter = 100, particles = 10, + mcmc_type = "is2", seed = 1, sampling_mcmc_type = "psi", + threads = 2L)[-16]) + expect_error(mcmc_sv <- run_mcmc(model_bssm, iter = 100, particles = 10, mcmc_type = "is2", seed = 1, sampling_mcmc_type = "bsf"), NA) - + + expect_warning(expand_sample(mcmc_sv)) + expect_error(summary(mcmc_sv, variable = "both"), NA) expect_gt(mcmc_sv$acceptance_rate, 0) expect_true(is.finite(sum(mcmc_sv$theta))) expect_true(is.finite(sum(mcmc_sv$alpha))) expect_gte(min(mcmc_sv$weights), 0) expect_lt(max(mcmc_sv$weights), Inf) + + expect_equal(run_mcmc(model_bssm, iter = 100, particles = 10, + mcmc_type = "pm", seed = 1, sampling_mcmc_type = "psi", + output_type = "summary")[-15], + run_mcmc(model_bssm, iter = 100, particles = 10, + mcmc_type = "pm", seed = 1, sampling_mcmc_type = "psi", + output_type = "summary")[-15]) + + expect_equal(run_mcmc(model_bssm, iter = 100, particles = 10, + mcmc_type = "is2", seed = 1, sampling_mcmc_type = "psi", + output_type = "summary", + threads = 2L)[-17], + run_mcmc(model_bssm, iter = 100, particles = 10, + mcmc_type = "is2", seed = 1, sampling_mcmc_type = "psi", + output_type = "summary", + threads = 2L)[-17]) +}) + +test_that("MCMC for nonlinear models work", { + skip_on_cran() + set.seed(1) + n <- 10 + x <- y <- numeric(n) + y[1] <- rnorm(1, exp(x[1]), 0.1) + for(i in 1:(n-1)) { + x[i+1] <- rnorm(1, sin(x[i]), 0.1) + y[i+1] <- rnorm(1, exp(x[i+1]), 0.1) + } + + pntrs <- cpp_example_model("nlg_sin_exp") + + expect_error(model_nlg <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(log_H = log(0.1), log_R = log(0.1)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state"), NA) + + + for(type in c("pm", "da", "is1", "is3", "is3", "approx", "ekf")) { + for(method in c("psi", "bsf", "ekf")) { + for(output in c("full", "summary", "theta")) { + if(type %in% c("is1", "is2", "is3") && method == "ekf") { + expect_error(run_mcmc(model_nlg, mcmc_type = type, + sampling_method = method, output_type = output, iter = 100, + seed = 1, particles = 5)) + } else { + expect_error( + run_mcmc(out <- model_nlg, mcmc_type = type, + sampling_method = method, output_type = output, iter = 100, + seed = 1, particles = 5), NA) + expect_equal(sum(is.na(out$theta)), 0) + expect_equal(sum(is.na(out$alpha)), 0) + expect_equal(sum(!is.finite(out$theta)), 0) + expect_equal(sum(!is.finite(out$alpha)), 0) + expect_equal(sum(!is.finite(out$posterior)), 0) + } + } + } + } + + expect_equal(run_mcmc(model_nlg, iter = 100, particles = 10, + mcmc_type = "is2", seed = 1, sampling_method = "psi", + threads = 2L)[-16], + run_mcmc(model_nlg, iter = 100, particles = 10, + mcmc_type = "is2", seed = 1, sampling_method = "psi", + threads = 2L)[-16]) + + expect_equal( + run_mcmc(model_nlg, iter = 100, particles = 10, + mcmc_type = "is1", seed = 1, sampling_method = "psi", + output_type = "summary", + threads = 2L)[-17], + run_mcmc(model_nlg, iter = 100, particles = 10, + mcmc_type = "is1", seed = 1, sampling_method = "psi", + output_type = "summary", + threads = 2L)[-17]) + }) diff --git a/tests/testthat/test_models.R b/tests/testthat/test_models.R index 15bbf0a8..4a658903 100644 --- a/tests/testthat/test_models.R +++ b/tests/testthat/test_models.R @@ -1,6 +1,9 @@ +#' @srrstats {G5.8, G5.8a, G5.8b, G5.8c, G5.8d, BS2.1, BS2.1a} + context("Test models") -test_that("bad argument values for bsm throws an error",{ +test_that("bad argument values for bsm throws an error", { + expect_error(bsm_lg(numeric(0), 1, 1)) expect_error(bsm_lg("character vector")) expect_error(bsm_lg(matrix(0, 2, 2))) expect_error(bsm_lg(1)) @@ -11,43 +14,56 @@ test_that("bad argument values for bsm throws an error",{ expect_error(bsm_lg(1:10, xreg = matrix(NA))) expect_error(bsm_lg(1:10, xreg = matrix(1:20), beta = uniform(0, 0, 1))) expect_error(bsm_lg(1:10, xreg = 1:10, beta = NA)) - expect_error(bsm_lg(1:10, 1, 1, 1, a1 = 1)) + expect_error(bsm_lg(1:10, 1, 1, 1, a1 = 1:4)) expect_error(bsm_lg(1:10, 1, 1, 1, 1)) }) -test_that("proper arguments for bsm don't throw an error",{ +test_that("proper arguments for bsm don't throw an error", { expect_error(bsm_lg(1:10, 1, 1), NA) expect_error(bsm_lg(1:10, uniform(0, 0, 1), 1), NA) expect_error(bsm_lg(1:10, 1, 1, uniform(0, 0, 1)), NA) expect_error(bsm_lg(1:10, 1, 1, 1, 1, period = 3), NA) - expect_error(bsm_lg(1:10, 1, 1, 1, 1, period = 3, xreg = matrix(1:10, 10), beta = normal(0,0,10)), NA) + expect_error(bsm_lg(1:10, 1, 1, 1, 1, period = 3, xreg = matrix(1:10, 10), + beta = normal(0, 0, 10)), NA) }) -test_that("bad argument values for bsm_ng throws an error",{ +test_that("bad argument values for bsm_ng throws an error", { + expect_error(bsm_ng(numeric(0), 1, 1, distribution = "poisson")) expect_error(bsm_ng("character vector", distribution = "poisson")) - expect_error(bsm_ng(matrix(0, 2, 2), distribution = "poisson")) + expect_error(bsm_ng(1:10, distribution = "poisson")) + expect_error(bsm_ng(diag(2), distribution = "poisson", + sd_level = 1)) expect_error(bsm_ng(1, distribution = "poisson")) expect_error(bsm_ng(c(1, Inf), distribution = "poisson")) expect_error(bsm_ng(1:10, sd_level = "character", distribution = "poisson")) - expect_error(bsm_ng(1:10, sd_y = Inf, distribution = "poisson")) + expect_error(bsm_ng(1:10, sd_level = Inf, distribution = "poisson")) expect_error(bsm_ng(1:10, no_argument = 5, distribution = "poisson")) - expect_error(bsm_ng(1:10, xreg = matrix(1:20), beta = uniform(0, 0, 1), distribution = "poisson")) - expect_error(bsm_ng(1:10, xreg = 1:10, beta = NA, distribution = "poisson")) - expect_error(bsm_ng(1:10, 1, 1, a1 = 1, distribution = "poisson")) - expect_error(bsm_ng(1:10, 1, 1, 1, 1, distribution = "poisson")) + expect_error(bsm_ng(1:10, 1, 1, xreg = matrix(1:20), beta = uniform(0, 0, 1), + distribution = "poisson")) + expect_error(bsm_ng(1:10, 1, 1, xreg = matrix(Inf, 10, 1), + beta = uniform(0, 0, 1), distribution = "poisson")) + expect_error(bsm_ng(1:10, 1, 1, xreg = 1:10, beta = NA, + distribution = "poisson")) + expect_error(bsm_ng(1:10, 1, 1, a1 = "a", distribution = "poisson")) + expect_error(bsm_ng(1:2, 1, 1, 1, distribution = "poisson", period = 2)) + expect_error(bsm_ng(-(1:2), 1, 1, distribution = "poisson")) + expect_error(bsm_ng(1:2 + 0.1, 1, 1,distribution = "poisson")) + expect_error(bsm_ng(1:2, 1, sd_y = halfnormal(0, 1:2), + distribution = "poisson")) }) -test_that("proper arguments for ng_bsm don't throw an error",{ +test_that("proper arguments for ng_bsm don't throw an error", { expect_error(bsm_ng(1:10, 1, 1, distribution = "poisson"), NA) + expect_error(bsm_ng(1:10, 1, 1, distribution = "POISSon"), NA) expect_error(bsm_ng(1:10, uniform(0, 0, 1), 1, distribution = "poisson"), NA) expect_error(bsm_ng(1:10, 1, uniform(0, 0, 1), distribution = "poisson"), NA) expect_error(bsm_ng(1:10, 1, 1, 1, period = 3, distribution = "poisson"), NA) expect_error(bsm_ng(1:10, 1, 1, 1, period = 3, xreg = matrix(1:10, 10), - beta = normal(0,0,10), distribution = "poisson"), NA) + beta = normal(0, 0, 10), distribution = "poisson"), NA) }) -test_that("bad argument values for svm throws an error",{ +test_that("bad argument values for svm throws an error", { expect_error(svm("character vector")) expect_error(svm(matrix(0, 2, 2))) expect_error(svm(1)) @@ -60,9 +76,9 @@ test_that("bad argument values for svm throws an error",{ expect_error(svm(1:10, 1, 1, a1 = 1)) }) -test_that("proper arguments for svm don't throw an error",{ - expect_error(svm(1:10, rho = uniform(0.9,-0.9, 0.99), - mu = normal(0, 0,2), sd_ar = halfnormal(1, 2)), NA) +test_that("proper arguments for svm don't throw an error", { + expect_error(svm(1:10, rho = uniform(0.9, -0.9, 0.99), + mu = normal(0, 0, 2), sd_ar = halfnormal(1, 2)), NA) }) test_that("multivariate non-gaussian model", { @@ -76,8 +92,22 @@ test_that("multivariate non-gaussian model", { ufun <- function(theta) { list(R = array(diag(exp(theta)), c(2, 2, 1))) } + + expect_error(mng_model <- ssm_mng(y = data.frame(1:4,1:4), Z = diag(2), + T = diag(2), R = 0.1 * diag(2), P1 = diag(2), distribution = "poisson", + init_theta = log(c(0.1, 0.1)), prior_fn = pfun, update_fn = ufun)) + + expect_error(mng_model <- ssm_mng(y = y - 10, Z = diag(2), T = diag(2), + R = 0.1 * diag(2), P1 = diag(2), distribution = "poisson", + init_theta = log(c(0.1, 0.1)), prior_fn = pfun, update_fn = ufun)) + + expect_error(ssm_mng(y = y + 0.1, Z = diag(2), T = diag(2), + R = 0.1 * diag(2), P1 = diag(2), distribution = "poisson", + init_theta = log(c(0.1, 0.1)), prior_fn = pfun, update_fn = ufun)) + + expect_error(mng_model <- ssm_mng(y = y, Z = diag(2), T = diag(2), R = 0.1 * diag(2), P1 = diag(2), distribution = "poisson", init_theta = log(c(0.1, 0.1)), prior_fn = pfun, update_fn = ufun), NA) expect_error(logLik(mng_model, particles = 10), NA) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test_particle_smoother.R b/tests/testthat/test_particle_smoother.R new file mode 100644 index 00000000..686c913c --- /dev/null +++ b/tests/testthat/test_particle_smoother.R @@ -0,0 +1,173 @@ + +context("Test that particle smoothers work") + +#' @srrstats {G5.9, G5.9a, G5.9b} + +test_that("Test that trivial noise does not affect particle_smoother", { + + expect_error(model_bsm <- bsm_lg(rep(1, 5), sd_level = 0.05, sd_slope = 0.01, + sd_y = 0.01, a1 = c(1, 0), P1 = diag(0.01, 2)), NA) + model_bsm2 <- model_bsm + model_bsm2$y <- model_bsm2$y + .Machine$double.eps + expect_equal( + particle_smoother(model_bsm, 1e5, seed = 1)$alphahat, + particle_smoother(model_bsm2, 1e5, seed = 1)$alphahat) +}) +test_that("Test that different seeds give comparable results", { + + expect_error(model_bsm <- bsm_lg(rep(1, 5), sd_level = 0.05, sd_slope = 0.01, + sd_y = 0.01, a1 = c(1, 0), P1 = diag(0.01, 2)), NA) + expect_equal( + particle_smoother(model_bsm, 1e5)$alphahat, + particle_smoother(model_bsm, 1e5)$alphahat, tolerance = 0.001) +}) + +test_that("Test that particle_smoother for LGSSM works as Kalman smoother", { + + expect_error(model_bsm <- bsm_lg(rep(1, 5), sd_level = 0.05, sd_slope = 0.01, + sd_y = 0.01, a1 = c(1, 0), P1 = diag(0.01, 2)), NA) + expect_equal(smoother(model_bsm)$alphahat, + particle_smoother(model_bsm, 1e5, seed = 1)$alphahat, tolerance = 1e-2) +}) + +test_that("Test that BSF&PSI particle_smoother for LGSSM are with MC error", { + + expect_error(model_bsm <- bsm_lg(rep(1, 5), sd_level = 0.05, sd_slope = 0.01, + sd_y = 0.01, a1 = c(1, 0), P1 = diag(0.01, 2)), NA) + expect_error(out1 <- + particle_smoother(model_bsm, 1e4, method = "psi", seed = 1), NA) + expect_error(out2 <- + particle_smoother(model_bsm, 1e4, method = "bsf", seed = 1), NA) + expect_equal(out1$alphahat, + out2$alphahat, tolerance = 1e-2) + expect_equal(out1$Vt, + out2$Vt, tolerance = 1e-2) +}) + +test_that("Particle smoother for LGSSM returns finite values", { + + expect_error(model_ssm_ulg <- ssm_ulg(y = 1:10, Z = matrix(c(1, 0), 2, 1), + H = 2, T = array(c(1, 0, 1, 1), c(2, 2, 1)), + R = array(diag(2, 2), c(2, 2, 1)), + a1 = matrix(0, 2, 1), P1 = diag(2, 2), state_names = c("level", "slope")), + NA) + expect_error(out <- particle_smoother(model_ssm_ulg, 10, seed = 1), + NA) + expect_true(is.finite(sum(out$alpha))) + expect_true(is.finite(sum(out$alphahat))) + expect_true(is.finite(sum(out$Vt))) +}) + +test_that("Particle smoother for poisson bsm_ng returns finite values", { + + expect_error(model <- bsm_ng(1:10, sd_level = 2, sd_slope = 2, + P1 = diag(2, 2), distribution = "poisson"), NA) + expect_error(out <- particle_smoother(model, 10, seed = 1), NA) + expect_error(out <- particle_smoother(model, 10, method = "bsf", seed = 1), + NA) + + expect_true(is.finite(sum(out$alpha))) + expect_true(is.finite(sum(out$alphahat))) + expect_true(is.finite(sum(out$Vt))) +}) + +test_that("Particle smoother for binomial bsm_ng returns finite values", { + + expect_error(model <- bsm_ng(c(1, 0, 1, 1, 1, 0, 0, 0), sd_level = 2, + sd_slope = 2, P1 = diag(2, 2), + distribution = "binomial"), NA) + expect_error(out <- particle_smoother(model, 10, seed = 1), NA) + + expect_true(is.finite(sum(out$alpha))) + expect_true(is.finite(sum(out$alphahat))) + expect_true(is.finite(sum(out$Vt))) + +}) + +test_that("Particle smoother for NB bsm_ng returns finite values", { + + expect_error(model <- bsm_ng(c(1, 0, 1, 1, 1, 0, 0, 0), + sd_level = uniform(0.1,0,1), + sd_slope = halfnormal(0.1, 1), + P1 = diag(2, 2), phi = gamma(1, 2, 2), + distribution = "negative binomial"), NA) + + expect_error(out <- particle_smoother(model, 10, seed = 1), NA) + expect_true(is.finite(sum(out$alpha))) + expect_true(is.finite(sum(out$alphahat))) + expect_true(is.finite(sum(out$Vt))) + + expect_error(out <- particle_smoother(model, 10, method = "bsf", seed = 1), + NA) + expect_true(is.finite(sum(out$alpha))) + expect_true(is.finite(sum(out$alphahat))) + expect_true(is.finite(sum(out$Vt))) + +}) + + +test_that("Particle smoother for svm returns finite values", { + + data("exchange") + model <- svm(exchange[1:20], rho = uniform(0.98, -1, 1), + sd_ar = halfnormal(0.01,0.1), mu = normal(0, 0, 1)) + + expect_error(out1 <- + particle_smoother(model, 100, method = "psi", seed = 1), NA) + expect_error(out2 <- + particle_smoother(model, 10000, method = "bsf", seed = 1), NA) + + expect_true(is.finite(sum(out1$alpha))) + expect_true(is.finite(sum(out1$alphahat))) + expect_true(is.finite(sum(out1$Vt))) + + expect_equal(out1$alphahat, out2$alphahat, tol = 0.1) +}) + +tol <- 1e-8 +test_that("Test that linear-gaussian bsf smoother still works", { + + expect_error(model_ssm_ulg <- ssm_ulg(y = 1:10, Z = matrix(c(1, 0), 2, 1), + H = 2, T = array(c(1, 0, 1, 1), c(2, 2, 1)), + R = array(diag(2, 2), c(2, 2, 1)), + a1 = matrix(0, 2, 1), P1 = diag(2, 2), state_names = c("level", "slope")), + NA) + expect_error(bsf_ssm_ulg <- particle_smoother(model_ssm_ulg, 10, seed = 1, + method = "bsf"), + NA) + expect_gte(min(bsf_ssm_ulg$weights), 0) + expect_lt(max(bsf_ssm_ulg$weights), Inf) + expect_true(is.finite(bsf_ssm_ulg$logLik)) + expect_true(is.finite(sum(bsf_ssm_ulg$alphahat))) + expect_true(is.finite(sum(bsf_ssm_ulg$Vt))) + + expect_error(model_ar1_lg <- ar1_lg(y = 1:10, + rho = tnormal(0.6, 0, 0.5, -1, 1), + sigma = gamma(1,2,2), sd_y = 0.1, mu = 1), NA) + expect_error(bsf_ar1_lg <- particle_smoother(model_ar1_lg, 10, seed = 1, + method = "bsf"), NA) + expect_gte(min(bsf_ar1_lg$weights), 0) + expect_lt(max(bsf_ar1_lg$weights), Inf) + expect_true(is.finite(bsf_ar1_lg$logLik)) + expect_true(is.finite(sum(bsf_ar1_lg$alphahat))) + expect_true(is.finite(sum(bsf_ar1_lg$Vt))) +}) + + +test_that("Test that binomial ar1_ng still works", { + + expect_error(model <- ar1_ng(c(1, 0, 1, 1, 1, 0, 0, 0), + rho = uniform(0.9, 0, 1), sigma = gamma(1, 2, 2), + mu = normal(1, 0, 1), + xreg = 1:8, beta = normal(0, 0, 0.1), + distribution = "binomial"), NA) + expect_error(bsf_binomial <- particle_smoother(model, 10, method = "bsf", + seed = 1), NA) + + expect_gte(min(bsf_binomial$weights), 0) + expect_lt(max(bsf_binomial$weights), Inf) + expect_true(is.finite(bsf_binomial$logLik)) + expect_true(is.finite(sum(bsf_binomial$alphahat))) + expect_true(is.finite(sum(bsf_binomial$Vt))) + +}) diff --git a/tests/testthat/test_post_correct.R b/tests/testthat/test_post_correct.R new file mode 100644 index 00000000..84a9c45b --- /dev/null +++ b/tests/testthat/test_post_correct.R @@ -0,0 +1,115 @@ + +context("Post-correction and suggest_N") + + +test_that("Test post correction for AR1 model", { + set.seed(1) + n <- 14 + x1 <- sin((2 * pi / 12) * 1:n) + x2 <- cos((2 * pi / 12) * 1:n) + alpha <- numeric(n) + alpha[1] <- 0 + rho <- 0.7 + sigma <- 2 + mu <- 1 + for(i in 2:n) { + alpha[i] <- rnorm(1, mu * (1 - rho) + rho * alpha[i-1], sigma) + } + u <- rpois(n, 50) + y <- rbinom(n, size = u, plogis(0.5 * x1 + x2 + alpha)) + + expect_error(model <- ar1_ng(y, distribution = "binomial", + rho = uniform(0.5, -1, 1), sigma = gamma(1, 2, 0.001), + mu = normal(0, 0, 10), + xreg = cbind(x1,x2), beta = normal(c(0, 0), 0, 5), + u = u), NA) + + + expect_error(out_approx <- run_mcmc(model, mcmc_type = "approx", + local_approx = FALSE, iter = 1000, output_type = "summary"), NA) + + expect_error(estN <- suggest_N(model, out_approx, + replications = 10, candidates = c(5, 10)), NA) + + expect_identical(estN$N, 5) + + + expect_error(post_correct(data.frame(1), out_approx, particles = estN$N, + threads = 2)) + expect_error(post_correct(model, out_approx, particles = estN$N, + threads = 2, particles = 1e12)) + expect_error(post_correct(model, out_approx, particles = estN$N, + threads = 2, particles = 10, theta = diag(2))) + expect_error(post_correct(model, out_approx, particles = estN$N, + threads = 2, particles = 10, theta = rep(1:6))) + expect_error(post_correct(model, 1:5, particles = estN$N, + threads = 2)) + + # Can't really test for correctness with limited time + expect_error(out_is2 <- post_correct(model, out_approx, particles = estN$N, + threads = 2), NA) + expect_lt(sum(out_is2$theta), Inf) + expect_lt(sum(out_is2$alphahat), Inf) + expect_lt(sum(out_is2$Vt), Inf) + expect_lt(max(out_is2$weights), Inf) + expect_gt(max(out_is2$weights), 0) + + +}) + +test_that("Test post correction for non-linear model", { + skip_on_cran() + set.seed(1) + + p1 <- 50 # population size at t = 1 + K <- 500 # carrying capacity + H <- 1 # standard deviation of obs noise + R_1 <- 0.05 # standard deviation of the noise on logit-growth + R_2 <- 1 # standard deviation of the noise in population level + #sample time + dT <- .1 + + #observation times + t <- seq(0.1, 10, dT) + n <- length(t) + r <- plogis(cumsum(c(-1.5, rnorm(n - 1, sd = R_1)))) + p <- numeric(n) + p[1] <- p1 + for(i in 2:n) + p[i] <- rnorm(1, K * p[i-1] * exp(r[i-1] * dT) / + (K + p[i-1] * (exp(r[i-1] * dT) - 1)), R_2) + # observations + y <- p + rnorm(n, 0, H) + y[2:15] <- NA + pntrs <- cpp_example_model("nlg_growth") + + initial_theta <- c(log_H = 0, log_R1 = log(0.05), log_R2 = 0) + + # dT, K, a1 and the prior variances of 1st and 2nd state (logit r and and p) + known_params <- c(dT = dT, K = K, a11 = -1, a12 = 50, P11 = 1, P12 = 100) + + expect_error(model <- ssm_nlg(y = y, a1=pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = initial_theta, log_prior_pdf = pntrs$log_prior_pdf, + known_params = known_params, known_tv_params = matrix(1), + n_states = 2, n_etas = 2, state_names = c("logit_r", "p")), NA) + + + expect_error(out_approx <- run_mcmc(model, mcmc_type = "approx", + local_approx = FALSE, iter = 1000, output_type = "full"), NA) + + expect_error(estN <- suggest_N(model, out_approx, + replications = 10, candidates = c(5, 10)), NA) + + expect_identical(estN$N, 5) + + # Can't really test for correctness with limited time + expect_error(out_is2 <- post_correct(model, out_approx, particles = estN$N, + threads = 2), NA) + expect_lt(sum(out_is2$theta), Inf) + expect_lt(sum(out_is2$alphahat), Inf) + expect_lt(sum(out_is2$Vt), Inf) + expect_lt(max(out_is2$weights), Inf) + expect_gt(max(out_is2$weights), 0) +}) diff --git a/tests/testthat/test_predict.R b/tests/testthat/test_predict.R new file mode 100644 index 00000000..272f5d8e --- /dev/null +++ b/tests/testthat/test_predict.R @@ -0,0 +1,297 @@ +context("Test predictions") + + +test_that("Gaussian predictions work", { + + set.seed(1) + y <- rnorm(10, cumsum(rnorm(10, 0, 0.1)), 0.1) + model <- ar1_lg(y, + rho = uniform(0.9, 0, 1), mu = 0, + sigma = halfnormal(0.1, 1), + sd_y = halfnormal(0.1, 1)) + + set.seed(123) + mcmc_results <- run_mcmc(model, iter = 1000) + future_model <- model + future_model$y <- rep(NA, 3) + set.seed(1) + pred <- predict(mcmc_results, future_model, type = "mean", + nsim = 100) + + expect_gt(mean(pred$value[pred$time == 3]), -0.5) + expect_lt(mean(pred$value[pred$time == 3]), 0.5) + + # Posterior predictions for past observations: + set.seed(1) + expect_error(yrep <- predict(mcmc_results, model, type = "response", + future = FALSE, nsim = 100), NA) + set.seed(1) + expect_error(meanrep <- predict(mcmc_results, model, type = "mean", + future = FALSE, nsim = 100), NA) + + expect_equal(mean(yrep$value - meanrep$value), 0, tol = 0.1) + + ufun <- function(x) { + T <- array(x[1]) + R <- array(exp(x[2])) + H <- array(exp(x[3])) + dim(T) <- dim(R) <- dim(H) <- c(1, 1, 1) + P1 <- matrix(exp(x[2])^2) / (1 - x[1]^2) + list(T = T, R = R, P1 = P1, H = H) + } + pfun <- function(x) { + ifelse(x[1] > 1 | x[1] < 0, -Inf, sum(-0.5 * exp(x[2:3])^2 + x[2:3])) + } + + expect_error(model2 <- ssm_mlg(matrix(model$y, length(model$y), 1), + Z = 1, H = model$H, T = model$T, R = model$R, + a1 = model$a1, P1 = model$P1, + init_theta = c(rho = 0.9, sigma = log(0.1), sd_y = log(0.1)), + update_fn = ufun, prior_fn = pfun, state_names = "signal"), NA) + + set.seed(123) + expect_error(mcmc_results2 <- run_mcmc(model2, iter = 1000), + NA) + # transform manually + mcmc_results2$theta[, 2:3] <- exp(mcmc_results2$theta[, 2:3]) + expect_equal(mcmc_results$theta, mcmc_results2$theta) + expect_equal(mcmc_results$alpha, mcmc_results2$alpha) + expect_equal(mcmc_results$posterior, mcmc_results2$posterior) + # transform back to predict... + mcmc_results2$theta[, 2:3] <- log(mcmc_results2$theta[, 2:3]) + future_model2 <- model2 + future_model2$y <- matrix(NA, 3, 1) + set.seed(1) + expect_error(pred2 <- predict(mcmc_results2, future_model2, type = "mean", + nsim = 100), NA) + expect_equal(pred, pred2) + # Posterior predictions for past observations: + set.seed(1) + expect_error(yrep2 <- predict(mcmc_results2, model2, type = "response", + future = FALSE, nsim = 100), NA) + set.seed(1) + expect_error(meanrep2 <- predict(mcmc_results2, model2, type = "mean", + future = FALSE, nsim = 100), NA) + expect_equal(yrep, yrep2) + expect_equal(meanrep, meanrep2) + + expect_error(predict(mcmc_results2, model, type = "response", + future = FALSE, nsim = 100)) + + expect_error(predict(mcmc_results2, model2, type = "response", + future = FALSE, nsim = 0)) + expect_error(predict(mcmc_results2, model2, type = "response", + future = 5, nsim = 100)) + expect_error(predict(mcmc_results2, model = 465, type = "response", + future = FALSE, nsim = 100)) + mcmc_results3 <- run_mcmc(model2, iter = 1000, output_type = "theta") + expect_error(predict(mcmc_results3, model2, type = "response", + future = FALSE, nsim = 100)) + class(model) <- "aa" + expect_error(predict(mcmc_results3, model2, type = "response", + future = FALSE, nsim = 100)) + + + set.seed(1) + y <- rnorm(10, cumsum(rnorm(10, 0, 0.1)), 0.1) + model <- bsm_lg(y, + sd_level = halfnormal(1, 1), + sd_slope = halfnormal(0.1, 0.1), + sd_y = halfnormal(0.1, 1)) + + mcmc_results <- run_mcmc(model, iter = 1000) + future_model <- model + future_model$y <- rep(NA, 3) + + expect_error(predict(mcmc_results, future_model, type = "mean", + nsim = 1000), paste0("The number of samples should be smaller than or ", + "equal to the number of posterior samples 500.")) + expect_error(predict(mcmc_results, future_model, type = "state", + nsim = 50), NA) + + set.seed(1) + expect_error(pred <- predict(mcmc_results, future_model, type = "mean", + nsim = 500), NA) + + expect_gt(mean(pred$value[pred$time == 3]), 0) + expect_lt(mean(pred$value[pred$time == 3]), 0.5) + + # Posterior predictions for past observations: + set.seed(1) + expect_error(yrep <- predict(mcmc_results, model, type = "response", + future = FALSE, nsim = 100), NA) + set.seed(1) + expect_error(meanrep <- predict(mcmc_results, model, type = "mean", + future = FALSE, nsim = 100), NA) + + expect_equal(mean(yrep$value - meanrep$value), 0, tol = 0.1) + + +}) + +test_that("Non-gaussian predictions work", { + + set.seed(1) + y <- rpois(10, exp(cumsum(rnorm(10, 0, 0.1)))) + model <- ar1_ng(y, + rho = uniform(0.9, 0, 1), mu = 0, + sigma = halfnormal(0.1, 1), distribution = "poisson") + + set.seed(123) + expect_error(mcmc_results <- run_mcmc(model, iter = 1000, particles = 5), NA) + future_model <- model + future_model$y <- rep(NA, 3) + set.seed(1) + expect_error(pred <- predict(mcmc_results, future_model, type = "mean", + nsim = 100), NA) + + expect_gt(mean(pred$value[pred$time == 3]), 1) + expect_lt(mean(pred$value[pred$time == 3]), 1.5) + + # Posterior predictions for past observations: + set.seed(1) + expect_error(yrep <- predict(mcmc_results, model, type = "response", + future = FALSE, nsim = 100), NA) + set.seed(1) + expect_error(meanrep <- predict(mcmc_results, model, type = "mean", + future = FALSE, nsim = 100), NA) + + expect_equal(mean(yrep$value - meanrep$value), 0, tol = 0.5) + + update_fn <- function(x) { + T <- array(x[1]) + R <- array(exp(x[2])) + dim(T) <- dim(R) <- c(1, 1, 1) + P1 <- matrix(exp(x[2])^2) / (1 - x[1]^2) + list(T = T, R = R, P1 = P1) + } + prior_fn <- function(x) { + ifelse(x[1] < 0 | x[1] > 1, -Inf, - 0.5 * exp(x[2])^2 + x[2]) + } + model2 <- ssm_ung(model$y, Z = 1, T = model$T, R = model$R, a1 = model$a1, + P1 = model$P1, distribution = "poisson", update_fn = update_fn, + prior_fn = prior_fn, init_theta = c(rho = 0.9, log(model$theta[2])), + state_names = "signal") + + set.seed(123) + expect_error(mcmc_results2 <- run_mcmc(model2, iter = 1000, particles = 5), + NA) + # transform manually + mcmc_results2$theta[, 2] <- exp(mcmc_results2$theta[, 2]) + expect_equal(mcmc_results$theta, mcmc_results2$theta) + expect_equal(mcmc_results$alpha, mcmc_results2$alpha) + expect_equal(mcmc_results$posterior, mcmc_results2$posterior) + + # transform back for predict + mcmc_results2$theta[, 2] <- log(mcmc_results2$theta[, 2]) + + future_model2 <- model2 + future_model2$y <- rep(NA, 3) + set.seed(1) + expect_error(pred2 <- predict(mcmc_results2, future_model2, type = "mean", + nsim = 100), NA) + expect_equal(pred, pred2) + # Posterior predictions for past observations: + set.seed(1) + expect_error(yrep2 <- predict(mcmc_results2, model2, type = "response", + future = FALSE, nsim = 100), NA) + set.seed(1) + expect_error(meanrep2 <- predict(mcmc_results2, model2, type = "mean", + future = FALSE, nsim = 100), NA) + expect_equal(yrep, yrep2) + expect_equal(meanrep, meanrep2) + expect_error(predict(mcmc_results2, model, type = "response", + future = FALSE, nsim = 100)) +}) + +test_that("Predictions for nlg_ssm work", { + skip_on_cran() + set.seed(1) + n <- 10 + x <- y <- numeric(n) + y[1] <- rnorm(1, exp(x[1]), 0.1) + for(i in 1:(n-1)) { + x[i+1] <- rnorm(1, 0.9 * x[i], 0.1) + y[i+1] <- rnorm(1, exp(x[i+1]), 0.1) + } + + pntrs <- cpp_example_model("nlg_ar_exp") + + expect_error(model <- ssm_nlg(y = y, a1 = pntrs$a1, P1 = pntrs$P1, + Z = pntrs$Z_fn, H = pntrs$H_fn, T = pntrs$T_fn, R = pntrs$R_fn, + Z_gn = pntrs$Z_gn, T_gn = pntrs$T_gn, + theta = c(mu = 0, rho = 0.9, log_R = log(0.1), log_H = log(0.1)), + log_prior_pdf = pntrs$log_prior_pdf, + n_states = 1, n_etas = 1, state_names = "state"), NA) + + expect_error(mcmc_results <- run_mcmc(model, iter = 5000, particles = 10), + NA) + future_model <- model + future_model$y <- rep(NA, 3) + expect_error(pred <- predict(mcmc_results, particles = 10, + future_model, type = "mean", nsim = 1000), NA) + + expect_gt(mean(pred$value[pred$time == 3]), 0.5) + expect_lt(mean(pred$value[pred$time == 3]), 1.5) + + # Posterior predictions for past observations: + set.seed(1) + expect_error(yrep <- predict(mcmc_results, model, type = "response", + future = FALSE, nsim = 100), NA) + set.seed(1) + expect_error(meanrep <- predict(mcmc_results, model, type = "mean", + future = FALSE, nsim = 100), NA) + + expect_equal(mean(yrep$value - meanrep$value), 0, tol = 0.5) +}) + + +test_that("Predictions for mng_ssm work", { + set.seed(1) + n <- 20 + x <- cumsum(rnorm(n, sd = 0.5)) + phi <- 2 + y <- cbind(rnbinom(n, size = phi, mu = exp(x)), + rpois(n, exp(x))) + + Z <- matrix(1, 2, 1) + T <- 1 + R <- 0.5 + a1 <- 0 + P1 <- 1 + + update_fn <- function(theta) { + list(R = array(theta[1], c(1, 1, 1)), phi = c(theta[2], 1)) + } + + prior_fn <- function(theta) { + ifelse(all(theta > 0), sum(dnorm(theta, 0, 1, log = TRUE)), -Inf) + } + + expect_error(model <- ssm_mng(y, Z, T, R, a1, P1, phi = c(2, 1), + init_theta = c(0.5, 2), + distribution = c("negative binomial", "poisson"), + update_fn = update_fn, prior_fn = prior_fn), NA) + + + expect_error(mcmc_results <- run_mcmc(model, iter = 5000, particles = 10), + NA) + future_model <- model + future_model$y <- matrix(NA, 3, 2) + expect_error(pred <- predict(mcmc_results, particles = 10, + future_model, type = "mean", nsim = 1000), NA) + + expect_gte(min(pred$value), 0) + expect_lt(max(pred$value), 1000) + + # Posterior predictions for past observations: + set.seed(1) + expect_error(yrep <- predict(mcmc_results, model, type = "response", + future = FALSE, nsim = 100), NA) + set.seed(1) + expect_error(meanrep <- predict(mcmc_results, model, type = "mean", + future = FALSE, nsim = 100), NA) + + expect_equal(mean(yrep$value - meanrep$value), 0, tol = 0.5) +}) + diff --git a/tests/testthat/test_priors.R b/tests/testthat/test_priors.R new file mode 100644 index 00000000..b237e5d7 --- /dev/null +++ b/tests/testthat/test_priors.R @@ -0,0 +1,20 @@ +context("Test rest of warnings and errors") + +#' @srrstats {G5.2, G5.2a, G5.2b} Test the rest of the warnings that are not +#' already triggered otherwise. +#' +test_that("priors give errors with wrong arguments", { + expect_error(normal("a", 0, 1)) + expect_error(uniform(1, 2, 0)) + expect_error(uniform(2, 0, 1)) + expect_error(normal(0, 0, -1)) + expect_error(halfnormal(0, -1)) + expect_error(halfnormal(-1, 0, 1)) + expect_error(tnormal(0, 0, -1)) + expect_error(tnormal(10, 0, 4, 0, 5)) + expect_error(gamma("a", 2, 1)) + expect_error(gamma(1, 0, 1)) + expect_error(gamma(1, -1, 1)) + expect_error(gamma(1, 2, 0)) +}) + diff --git a/tests/testthat/test_sde.R b/tests/testthat/test_sde.R new file mode 100644 index 00000000..79622d6b --- /dev/null +++ b/tests/testthat/test_sde.R @@ -0,0 +1,79 @@ +context("Test SDE") +#' @srrstats {G5.0, G5.1, G5.4, G5.4a, G5.4b, G5.4c, BS7.2} GBM model and data +#' as in Vihola, Helske, Franks (2020) +test_that("MCMC for SDE works", { + skip_on_cran() + + pntrs <- cpp_example_model("sde_gbm") + set.seed(42) + n <- 50 + dt <- 1 + mu <- 0.05 + sigma_x <- 0.3 + sigma_y <- 1 + x <- 1 + y <- numeric(n) + for (k in 1:n) { + x <- x * exp((mu - 0.5 * sigma_x^2) * dt + + sqrt(dt) * rnorm(1, sd = sigma_x)) + y[k] <- rnorm(1, log(x), sigma_y) + } + + model <- ssm_sde(y, pntrs$drift, pntrs$diffusion, + pntrs$ddiffusion, pntrs$obs_density, + pntrs$prior, c(mu = 0.08, sigma_x = 0.4, sigma_y = 1.5), + x0 = 1, positive = TRUE) + + expect_error(out <- run_mcmc(model, iter = 2e4, burnin = 5000, + particles = 50, mcmc_type = "is2", + L_c = 2, L_f = 6, threads = 2), NA) + + paper <- c(0.053, 0.253, 1.058, 1.254, 2.960) + expect_equivalent(weighted_mean(out$theta, out$weights * out$counts), + paper[1:3], tol = 0.1) + expect_equivalent(weighted_mean(t(out$alpha[c(1,50),1,]), + out$weights * out$counts), paper[4:5], tol = 0.1) + + expect_error(out <- run_mcmc(model, iter = 2e4, burnin = 5000, + particles = 50, mcmc_type = "is2", + L_c = 2, L_f = 6, threads = -1)) + + expect_error(out <- run_mcmc(model, iter = 2e4, burnin = 5000, + particles = 50, mcmc_type = "is2", + L_c = 2, L_f = -1)) + + expect_error(out <- run_mcmc(model, iter = 2e4, burnin = 5000, + particles = 50, mcmc_type = "is2", + L_c = 2, L_f = 1)) + + expect_error(out <- run_mcmc(model, iter = 2e4, burnin = 5000, + particles = 50, mcmc_type = "pm", L_c = 0)) + + expect_error(bootstrap_filter(model, 1000, L = -2)) + expect_error(particle_smoother(model, 1000, L = 0)) + expect_error(ll <- logLik(model, 10000, L = -3)) + expect_error(ll <- logLik(model, 10000, L = 3), NA) + expect_equal(ll, -17, tol = 1) + expect_error(out_bsf <- bootstrap_filter(model, 1000, L = 3), NA) + expect_equal(ll, out_bsf$logLik, tol = 1) + + expect_error(out <- run_mcmc(model, iter = 500, + particles = 10, mcmc_type = "pm", L_f = 2), NA) + expect_gt(out$acceptance_rate, 0) + + expect_error(out <- run_mcmc(model, iter = 500, + particles = 10, mcmc_type = "da", L_c = 2, L_f = 3), NA) + expect_gt(out$acceptance_rate, 0) + + expect_error(out2 <- run_mcmc(model, iter = 500, + particles = 10, mcmc_type = "is2", L_c = 1, L_f = 2), NA) + + expect_gt(out2$acceptance_rate, 0) + expect_equal(mean(colMeans(out$theta)-colMeans(out2$theta)), 0, tol = 1) + + expect_error(out2 <- run_mcmc(model, iter = 500, + particles = 10, mcmc_type = "is1", L_c = 1, L_f = 2, threads = 2), NA) + + expect_gt(out2$acceptance_rate, 0) + expect_equal(mean(colMeans(out$theta)-colMeans(out2$theta)), 0, tol = 1) +}) diff --git a/tests/testthat/test_sim_smoother.R b/tests/testthat/test_sim_smoother.R new file mode 100644 index 00000000..395b9998 --- /dev/null +++ b/tests/testthat/test_sim_smoother.R @@ -0,0 +1,31 @@ +context("Test that simulation smoother work") + + +test_that("Test that sim_smoother for LGSSM works as Kalman smoother", { + y <- c(0.89, -0.05, -1.9, -1.9, 1.77, -0.22) + expect_error(model_bsm <- bsm_lg(y, sd_level = 1, sd_slope = 0.01, + sd_y = 1, a1 = c(0, 0), P1 = diag(2)), NA) + expect_error(sims <- sim_smoother(model_bsm, nsim = 10, + use_antithetic = TRUE), NA) + expect_equal(smoother(model_bsm)$alphahat, + as.ts(apply(sims, 1:2, mean))) + expect_error(sims <- sim_smoother(model_bsm, nsim = 10, + use_antithetic = "blaa")) + expect_error(sims <- sim_smoother(model_bsm, nsim = 10, + use_antithetic = 1)) +}) + + +test_that("sim_smoother for non-gaussian model works as Kalman smoother", { + + y <- c(11, 3, 1, 1, 354, 2) + expect_error(model <- bsm_ng(y, sd_level = 1, + sd_seasonal = 0.1, period = 4, + P1 = diag(c(1, 0.1, 0.1, 0.1)), distribution = "poisson"), NA) + expect_error(sims <- sim_smoother(model, nsim = 10, + use_antithetic = TRUE), NA) + expect_equal(smoother(model)$alphahat, + as.ts(apply(sims, 1:2, mean))) +}) + + diff --git a/vignettes/bssm.Rmd b/vignettes/bssm.Rmd index ebdaafe6..c14c068b 100644 --- a/vignettes/bssm.Rmd +++ b/vignettes/bssm.Rmd @@ -14,9 +14,11 @@ vignette: | --- ```{r, echo = FALSE} +Sys.setenv("OMP_NUM_THREADS" = 2) # For CRAN if (!requireNamespace("rmarkdown") || !rmarkdown::pandoc_available("1.12.3")) { - warning(call. = FALSE, "These vignettes assume rmarkdown and pandoc version 1.12.3. These were not found. Older versions will not work.") + warning(call. = FALSE, "These vignettes assume rmarkdown and pandoc version", + "1.12.3. These were not found. Older versions will not work.") knitr::knit_exit() } ``` @@ -25,11 +27,16 @@ if (!requireNamespace("rmarkdown") || knitr::opts_chunk$set(echo = TRUE) ``` +This is a short vignette illustrating the `bssm` package. For more detailed exposition, please see the corresponding R Journal paper: + +Jouni Helske and Matti Vihola (2021). "bssm: Bayesian Inference of Non-linear and Non-Gaussian State Space Models in R". The R Journal (2021) 13:2, pages 578-589. [Link to the paper](https://journal.r-project.org/archive/2021/RJ-2021-103/index.html). + + # Introduction State space models (SSM) are latent variable models which are commonly applied in analysing time series data due to their flexible and general framework [cf. @DK2012]. For `R` [@r-core], there is large number of packages available for state space modelling, especially for the two special cases. First special case is linear-Gaussian SSM (LGSSM) where both the observation and state densities are Gaussian with linear relationships with the states. Another special case is SSM with discrete state space, which are sometimes called hidden Markov models (HMM). What is special about these two classes of models is that the marginal likelihood function, and the conditional state distributions (conditioned on the observations) of these models are analytically tractable, making inference relatively straightforward. See for example [@Petris2010, @Tusell2010, @KFAS, @seqHMM] for review of some of the `R` packages dealing with these type of models. The `R` package `bssm` is designed for Bayesian inference of general state space models with non-Gaussian and/or non-linear observational and state equations. The package aims to provide easy-to-use and efficient functions for fully Bayesian inference of common time series models such basic structural time series model (BSM) [@Harvey1989] with exogenous covariates, simple stochastic volatility models, and discretized diffusion models, making it straightforward and efficient to make predictions and other inference in a Bayesian setting. -The motivation behind the `bssm` package is in [@vihola-helske-franks] which suggests a new computationally efficient, parallelisable approach for Bayesian inference of state space models. The core idea is to use fast approximate Markov chain Monte Carlo (MCMC) targeting the approximate marginal posterior of the hyperparameters, which is then used in importance sampling type weighting phase which provides asymptotically exact samples from the joint posterior of hyperparameters and the hidden states. In addition to this the two-stage procedure, standard pseudo-marginal MCMC and so called delayed acceptance pseudo-marginal MCMC are also supported. For more details, see [@helske-vihola2021]. There is also separate vignette for nonlinear models as well as for discretized diffusion models. +The motivation behind the `bssm` package is in [@vihola-helske-franks] which suggests a new computationally efficient, parallelisable approach for Bayesian inference of state space models. The core idea is to use fast approximate Markov chain Monte Carlo (MCMC) targeting the approximate marginal posterior of the hyperparameters (i.e. unknown variables excluding latent state variables), which is then used in importance sampling type weighting phase which provides asymptotically exact samples from the joint posterior of hyperparameters and the hidden states. In addition to this the two-stage procedure, standard pseudo-marginal MCMC and so called delayed acceptance pseudo-marginal MCMC are also supported. For more details, see [@helske-vihola2021]. There is also separate vignette for nonlinear models as well as for discretized diffusion models. ## State space models with linear-Gaussian dynamics @@ -58,7 +65,7 @@ where $d_t$ is a again known input, $x_t$ contains the exogenous covariate value For stochastic volatility model, there are two possible parameterizations available. In general for we have $$ -y_t = x'_t\beta + \sigma \exp(\alpha_t / 2)\epsilon_t, \quad \epsilon_t \sim N(0, 1), +y_t = \sigma \exp(\alpha_t / 2)\epsilon_t, \quad \epsilon_t \sim N(0, 1), $$ and $$ @@ -135,7 +142,7 @@ Here we use helper function `halfnormal` which defines half-Normal prior distrib For non-Gaussian models, function `bsm_ng` can be used for constructing an BSM model where the observations are assumed to be distributed according to Poisson, binomial, negative binomial, or Gamma distribution. The syntax is nearly identical as in case of `bsm_lg`, but we now define also the distribution via argument `distribution`, and depending on the model, we can also define parameters `u` and `phi`. For Poisson and negative binomial models, the known parameter `u` corresponds to the offset term, whereas in case of binomial model `u` defines the number of trials. For negative binomial model, argument `phi` defines the dispersion term, which can be given as a fixed value, or as a prior function. For same observational densities, a model where the state equation follows a first order autoregressive process can be defined using the function `ng_ar1`. Finally, a stochastic volatility model can be defined using a function `svm`, and an arbitrary linear-Gaussian state model with Poisson, binomial or negative binomial distributed observations can be defined with `ssm_ung` and `ssm_mng` for univariate and multivariate models respectively. -For models where the state equation is no longer linear-Gaussian, we can use our pointer-based C++ interface with the function `ssm_nlg`. Diffusion models can be defined with the function `ssm_sde`. +For models where the state equation is no longer linear-Gaussian, we can use our pointer-based C++ interface with the function `ssm_nlg`. Diffusion models can be defined with the function `ssm_sde`. For details regarding these types of models, see the corresponding vignettes `growth_model` and `sde_model` respectively. ## Filtering and smoothing @@ -173,9 +180,9 @@ ggplot(d, aes(x = value)) + ```{r trend, dev.args=list(pointsize = 10), fig.cap="Smoothed trend component with 95% intervals."} suppressMessages(library("dplyr")) d <- as.data.frame(mcmc_bsm, variable = "states") -level_fit <- d %>% - filter(variable == "level") %>% - group_by(time) %>% +level_fit <- d |> + filter(variable == "level") |> + group_by(time) |> summarise(consumption = mean(value), lwr = quantile(value, 0.025), upr = quantile(value, 0.975)) diff --git a/vignettes/bssm.bib b/vignettes/bssm.bib index af5200a1..e955682a 100644 --- a/vignettes/bssm.bib +++ b/vignettes/bssm.bib @@ -2,9 +2,7 @@ @article{helske-vihola2021 title={bssm: Bayesian Inference of Non-linear and Non-Gaussian State Space Models in {R}}, author={Helske, Jouni and Vihola, Matti}, year={2021}, - eprint={2101.08492}, - archivePrefix={arXiv}, - primaryClass={stat.CO}, + journal={R Journal}, url ={https://arxiv.org/abs/2101.08492} } @article{Neal2011, @@ -112,6 +110,13 @@ @Article{pitt-shephard1999 publisher = {Blackwell Publishers Ltd}, url = {https://dx.doi.org/10.1111/1467-9892.00126}, } + @Manual{diagis, + title = {{diagis}: Diagnostic Plot and Multivariate Summary Statistics of Weighted Samples from Importance Sampling}, + author = {Jouni Helske}, + note = {R package version 0.2.2}, + url = {https://github.com/helske/diagis}, + } + @Article{coda, title = {{CODA}: Convergence Diagnosis and Output Analysis for {MCMC}}, diff --git a/vignettes/growth_model.Rmd b/vignettes/growth_model.Rmd index 3a15586c..2120303f 100644 --- a/vignettes/growth_model.Rmd +++ b/vignettes/growth_model.Rmd @@ -10,10 +10,11 @@ vignette: | %\VignetteIndexEntry{Non-linear models with bssm} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} - %\VignetteDepends{ggplot2, dplyr, Hmisc} + %\VignetteDepends{ggplot2, dplyr} --- ```{r, echo = FALSE} +Sys.setenv("OMP_NUM_THREADS" = 2) # For CRAN if (!requireNamespace("rmarkdown") || !rmarkdown::pandoc_available("1.12.3")) { warning(call. = FALSE, "These vignettes assume rmarkdown and pandoc version 1.12.3. These were not found. Older versions will not work.") @@ -38,7 +39,7 @@ y_t = Z(t, \alpha_t, \theta) + H(t, \alpha_t, \theta)\epsilon_t,\\ $$ with $t=1,\ldots, n$, $\epsilon_t \sim N(0,\textrm{I}_p)$, and $\eta \sim N(0,\textrm{I}_k)$. Here vector $\theta$ contains the unknown model parameters. -As some of the model matrices may depend on the current state $\alpha_t$, constructing for example $T(t,\alpha_t,\theta)$ by calling user-defined `R` function is not feasible, as this should be done repeatedly within the particle filter which would negate the benefits of the whole `C++` implementation of the particle filter and Markov chain Monte Carlo. Therefore the functions $T(\cdot)$, $H(\cdot)$, $T(\cdot)$, $R(\cdot)$,$a_1(\cdot)$, $P_1(\cdot)$, as well as functions defining the Jacobians of $Z(\cdot)$ and $T(\cdot)$ and the prior distribution for $\theta$ must be defined by user as a external pointers to `C++` functions. +As some of the model matrices may depend on the current state $\alpha_t$, constructing for example $T(t,\alpha_t,\theta)$ by calling user-defined `R` function is not feasible, as this should be done repeatedly within the particle filter which would negate the benefits of the whole `C++` implementation of the particle filter and Markov chain Monte Carlo. Therefore the functions $T(\cdot)$, $H(\cdot)$, $T(\cdot)$, $R(\cdot)$,$a_1(\cdot)$, $P_1(\cdot)$, as well as functions defining the Jacobians of $Z(\cdot)$ and $T(\cdot)$ and the prior distribution for $\theta$ must be defined by user as a external pointers to `C++` functions. For the log-density of theta, we can call R's own C-level density functions, for example `R::dnorm`` (see the template for an example). As an example, a logistic growth model of form $$ @@ -77,7 +78,7 @@ y <- p + rnorm(n, 0, H) ## Model in bssm -The functions determining the model functions are given in file `model_functions.cpp`. For example, function `T_fn` defines the state transition function $T(\cdot)$: +The functions determining the model need to be written in C++. Some example models which can be used as a template are given by the function `cpp_example_model` which returns pointers usable as an input to `nlg_ssm`. For this growth model, we could call `cpp_example_model("nlg_growth")`. In general, you need to define the functions matching the model components, log-density of the prior and few other functions. For example, in case of our model, the function `T_fn` defines the state transition function $T(\cdot)$: ```{Rcpp, eval = FALSE} // [[Rcpp::export]] @@ -96,7 +97,7 @@ arma::vec T_fn(const unsigned int t, const arma::vec& alpha, const arma::vec& th } ``` -The name of this function does not matter, but it should always return Armadillo vector (`arma::vec`), and have the same signature (i.e. the order and types of the function's parameters) should always be like above, even though some of the parameters were not used in the body of the function. Note that all of these functions can also depend on some known parameters, given as `known_params` (vector) and `known_tv_params` (matrix) arguments to `ssm_nlg` function (which are then passed to individual `C++` snippets). For details of using Armadillo, see [Armadillo documentation](http://arma.sourceforge.net/docs.html). After defining the appropriate model functions, the `cpp` file should also contain a function for creating external pointers for the aforementioned functions. Why this is needed is more technical issue, but fortunately you can just copy the function from the example file without any modifications. +The name of this function does not matter, but it should always return Armadillo vector (`arma::vec`), and have the same signature (i.e. the order and types of the function's parameters) should always be like above, even though some of the parameters were not used in the body of the function. Note that all of these functions can also depend on some known parameters, given as `known_params` (vector) and `known_tv_params` (matrix) arguments to `ssm_nlg` function (which are then passed to individual `C++` snippets). For details of using Armadillo, see [Armadillo documentation](https://arma.sourceforge.net/docs.html). After defining the appropriate model functions, the `cpp` file should also contain a function for creating external pointers for the aforementioned functions. Why this is needed is more technical issue, but fortunately you can just copy the function from the example file without any modifications. After creating the file for `C++` functions, you need to compile the file using `Rcpp`^[As repeated calls to compile same `cpp` file can sometimes lead to memory issues, it is good practice to define unique cache directory using the `cacheDir` argument([see issue in Github](https://github.com/helske/crashtest/issues/1)). But the CRAN does not like this approach so we do not use it here.]: @@ -105,12 +106,12 @@ Rcpp::sourceCpp("ssm_nlg_template.cpp") pntrs <- create_xptrs() ``` -This takes a few seconds. let's define our initial guess for $\theta$, the logarithms of the standard deviations of observational and process level noise, and define the prior distribution for $\alpha_1(we use log-scale in sampling for efficiency reasons, but define priors for the standard deviations, see the template file)$: +This takes a few seconds. let's define our initial guess for $\theta$, the logarithms of the standard deviations of observational and process level noise, and define the prior distribution for $\alpha_1$ (we use log-scale in sampling for efficiency reasons, but define priors for the standard deviations, see the template file at the appendix): ```{r theta} initial_theta <- c(log_H = 0, log_R1 = log(0.05), log_R2 = 0) -# dT, K, a1 and the prior variances of first and second state (logit r and and p) +# dT, K, a1 and the prior variances of 1st and 2nd state (logit r and and p) known_params <- c(dT = dT, K = K, a11 = -1, a12 = 50, P11 = 1, P12 = 100) ``` @@ -136,8 +137,10 @@ Let's first run Extended Kalman filter and smoother using our initial guess for ```{r ekf} out_filter <- ekf(model) out_smoother <- ekf_smoother(model) -ts.plot(cbind(y, out_filter$att[, 2], out_smoother$alphahat[, 2]), col = 1:3) -ts.plot(plogis(cbind(out_filter$att[, 1], out_smoother$alphahat[, 1])), col = 1:2) +ts.plot(cbind(y, out_filter$att[, 2], + out_smoother$alphahat[, 2]), col = 1:3) +ts.plot(plogis(cbind(out_filter$att[, 1], + out_smoother$alphahat[, 1])), col = 1:2) ``` ## Markov chain Monte Carlo @@ -156,33 +159,33 @@ summary(mcmc_is, return_se = TRUE) summary(mcmc_ekf, return_se = TRUE) ``` -Using the `as.data.frame` method we can convert the state samples to a data frame for further processing with the `dplyr` package [@dplyr]: +Using the `as.data.frame` method we can convert the state samples to a data frame for further processing with the `dplyr` package [@dplyr] (we could do this automatically with `summary` method as well): ```{r summaries} library("dplyr") -library("Hmisc") +library("diagis") d1 <- as.data.frame(mcmc_is, variable = "states") d2 <- as.data.frame(mcmc_ekf, variable = "states") d1$method <- "is2-psi" d2$method <- "approx ekf" -r_summary <- rbind(d1, d2) %>% - filter(variable == "logit_r") %>% - group_by(time, method) %>% +r_summary <- rbind(d1, d2) |> + filter(variable == "logit_r") |> + group_by(time, method) |> summarise( - mean = wtd.mean(plogis(value), weight, normwt = TRUE), - lwr = wtd.quantile(plogis(value), weight, 0.025, normwt = TRUE), - upr = wtd.quantile(plogis(value), weight, 0.975, normwt = TRUE)) + mean = weighted_mean(plogis(value), weight), + lwr = weighted_quantile(plogis(value), weight, 0.025), + upr = weighted_quantile(plogis(value), weight, 0.975)) -p_summary <- rbind(d1, d2) %>% - filter(variable == "p") %>% - group_by(time, method) %>% +p_summary <- rbind(d1, d2) |> + filter(variable == "p") |> + group_by(time, method) |> summarise( - mean = wtd.mean(value, weight, normwt = TRUE), - lwr = wtd.quantile(value, weight, 0.025, normwt = TRUE), - upr = wtd.quantile(value, weight, 0.975, normwt = TRUE)) + mean = weighted_mean(value, weight), + lwr = weighted_quantile(value, weight, 0.025), + upr = weighted_quantile(value, weight, 0.975)) ``` -Above we used the weighted versions of mean and quantile functions provided by the `Hmisc` [@hmisc] package as our IS-MCMC algorithm produces weighted samples of the posterior. Alternatively, we could have used argument `output_type = "summary"`, in which case the `run_mcmc` returns posterior means and covariances of the states instead of samples (these are computed using the full output of particle filter so these estimates are more accurate). +Above we used the weighted versions of mean and quantile functions provided by the `diagis`[@diagis] package as our IS-MCMC algorithm produces weighted samples of the posterior. Alternatively, we could have used argument `output_type = "summary"`, in which case the `run_mcmc` returns posterior means and covariances of the states instead of samples (these are computed using the full output of particle filter so these estimates are more accurate). Using `ggplot2` [@ggplot2] we can compare our two estimation methods: ```{r figures} @@ -213,7 +216,7 @@ mcmc_ekf$time ## Appendix -This is the full `ssm_nlg_template.cpp` file: +This is the full `ssm_nlg_template.cpp` file (identical with `nlg_growth` accessible with `cpp_example_model`): ```{Rcpp ssm_nlg_template, code=readLines('ssm_nlg_template.cpp'), eval = FALSE, echo = TRUE} ``` diff --git a/vignettes/psi_pf.Rmd b/vignettes/psi_pf.Rmd index 3790d219..e5cfc46c 100644 --- a/vignettes/psi_pf.Rmd +++ b/vignettes/psi_pf.Rmd @@ -11,7 +11,12 @@ bvignette: | %\VignetteDepends{dplyr} --- +```{r srr-tags, eval = FALSE, echo = FALSE} +#' @srrstats {G5.0, G5.1} Codes for generating the data are included in in this Rmd file. +``` + ```{r, echo = FALSE} +Sys.setenv("OMP_NUM_THREADS" = 2) # For CRAN if (!requireNamespace("rmarkdown") || !rmarkdown::pandoc_available("1.12.3")) { warning(call. = FALSE, "These vignettes assume rmarkdown and pandoc version 1.12.3. These were not found. Older versions will not work.") @@ -142,7 +147,8 @@ growth_model_experiment <- function(n_cores, nsim, particles) { p <- numeric(n) p[1] <- p1 for(i in 2:n) - p[i] <- rnorm(1, K * p[i-1] * exp(r[i-1] * dT) / (K + p[i-1] * (exp(r[i-1] * dT) - 1)), 1) + p[i] <- rnorm(1, K * p[i-1] * exp(r[i-1] * dT) / + (K + p[i-1] * (exp(r[i-1] * dT) - 1)), 1) # observations y <- p + rnorm(n, 0, 1) @@ -154,7 +160,8 @@ growth_model_experiment <- function(n_cores, nsim, particles) { cl<-makeCluster(n_cores) registerDoParallel(cl) - results <- foreach (j = 1:n_cores, .combine = "rbind", .packages = "bssm") %dopar% { + results <- foreach (j = 1:n_cores, .combine = "rbind", + .packages = "bssm") %dopar% { Rcpp::sourceCpp("growth_model_functions.cpp") pntrs <- create_xptrs() @@ -167,17 +174,20 @@ growth_model_experiment <- function(n_cores, nsim, particles) { bsf <- ekpf <- psi <- matrix(NA, 10, nsim / n_cores) - for(i in 1:ncol(bsf)) { + for(i in seq_len(ncol(bsf))) { - time <- system.time(out <- particle_smoother(model, particles = particles, method = "bsf"))[3] + time <- system.time(out <- particle_smoother(model, + particles = particles, method = "bsf"))[3] bsf[, i] <- c(out$logLik, out$alphahat[1, ], diag(out$Vt[, , 1]), out$alphahat[n, ], diag(out$Vt[, , n]), time) - time <- system.time(out <- particle_smoother(model, particles = particles, method = "psi"))[3] + time <- system.time(out <- particle_smoother(model, + particles = particles, method = "psi"))[3] psi[, i] <- c(out$logLik, out$alphahat[1, ], diag(out$Vt[, , 1]), out$alphahat[n, ], diag(out$Vt[, , n]), time) - time <- system.time(out <- particle_smoother(model, particles = particles, method = "ekf"))[3] + time <- system.time(out <- particle_smoother(model, + particles = particles, method = "ekf"))[3] ekpf[, i] <- c(out$logLik, out$alphahat[1, ], diag(out$Vt[, , 1]), out$alphahat[n, ], diag(out$Vt[, , n]), time) } @@ -218,7 +228,8 @@ r <- plogis(cumsum(c(-1.5, rnorm(n - 1, sd = 0.05)))) p <- numeric(n) p[1] <- p1 for(i in 2:n) - p[i] <- rnorm(1, K * p[i-1] * exp(r[i-1] * dT) / (K + p[i-1] * (exp(r[i-1] * dT) - 1)), 1) + p[i] <- rnorm(1, K * p[i-1] * exp(r[i-1] * dT) / + (K + p[i-1] * (exp(r[i-1] * dT) - 1)), 1) # observations y <- p + rnorm(n, 0, 1) @@ -261,11 +272,11 @@ IRE <- function(x, time) { mean((x - truth)^2) * mean(time) } truth <- reference["logLik"] -sumr <- results %>% group_by(method, N) %>% +sumr <- results|> group_by(method, N)|> summarise(mean = mean(logLik), SD = sd(logLik), IRE = IRE(logLik, time), time = mean(time)) -table1 <- sumr %>% arrange(N) %>% knitr::kable(digit = 4, - caption = "Results for the log-likelihood estimates of the growth model. ") +table1 <- sumr|> arrange(N)|> knitr::kable(digit = 4, + caption = "Results for the log-likelihood estimates of the growth model. ") saveRDS(table1, file = "psi_pf_experiments/table1.rds") ``` ```{r tabl21, echo = FALSE} @@ -275,11 +286,11 @@ readRDS("psi_pf_experiments/table1.rds") Similar table for the smoothed estimate of $p_1$ show again the superiority of the $\psi$-PF, with no clear differences between BSF and EKPF. ```{r alpha, echo = FALSE, eval = FALSE} truth <- reference["alpha_11"] -sumr <- results %>% group_by(method, N) %>% +sumr <- results|> group_by(method, N)|> summarise(mean = mean(alpha_11), SD = sd(alpha_11), IRE = IRE(alpha_11, time), time = mean(time)) -table2 <- sumr %>% arrange(N) %>% knitr::kable(digit = 4, +table2 <- sumr|> arrange(N)|> knitr::kable(digit = 4, caption = "Results for the p_1 estimates of the growth model. ") saveRDS(table2, file = "psi_pf_experiments/table2.rds") ``` @@ -312,7 +323,8 @@ ar_exp_model_experiment <- function(n_cores, nsim, particles, theta) { cl<-makeCluster(n_cores) registerDoParallel(cl) - results <- foreach (j = 1:n_cores, .combine = "rbind", .packages = "bssm") %dopar% { + results <- foreach (j = 1:n_cores, .combine = "rbind", + .packages = "bssm") %dopar% { Rcpp::sourceCpp("ar_exp_model_functions.cpp") pntrs <- create_xptrs() @@ -327,16 +339,19 @@ ar_exp_model_experiment <- function(n_cores, nsim, particles, theta) { bsf <- ekpf <- psi <- matrix(NA, 6, nsim / n_cores) - for(i in 1:ncol(bsf)) { - time <- system.time(out <- particle_smoother(model, particles = particles, method = "bsf"))[3] + for(i in seq_len(ncol(bsf))) { + time <- system.time(out <- particle_smoother(model, + particles = particles, method = "bsf"))[3] bsf[, i] <- c(out$logLik, out$alphahat[1, ], out$Vt[, , 1], out$alphahat[n, ], out$Vt[, , n], time) - time <- system.time(out <- particle_smoother(model, particles = particles, method = "psi"))[3] + time <- system.time(out <- particle_smoother(model, + particles = particles, method = "psi"))[3] psi[, i] <- c(out$logLik, out$alphahat[1, ], out$Vt[, , 1], out$alphahat[n, ], out$Vt[, , n], time) - time <- system.time(out <- particle_smoother(model, particles = particles, method = "ekf"))[3] + time <- system.time(out <- particle_smoother(model, + particles = particles, method = "ekf"))[3] ekpf[, i] <- c(out$logLik, out$alphahat[1, ], out$Vt[, , 1], out$alphahat[n, ], out$Vt[, , n], time) } @@ -401,11 +416,11 @@ Table 4 shows the means and standard deviations of the log-likelihood estimates ```{r loglik_ar, echo = FALSE, eval = FALSE} reference <- readRDS("psi_pf_experiments/ar_truth.rds") truth <- reference["logLik"] -sumr <- results %>% group_by(method, N) %>% +sumr <- results|> group_by(method, N)|> summarise(mean = mean(logLik), SD = sd(logLik), IRE = IRE(logLik, time), time = mean(time)) -table3 <- sumr %>% arrange(N) %>% knitr::kable(digit = 4, - caption = "Results for the log-likelihood estimates of the AR model. ") +table3 <- sumr|> arrange(N)|> knitr::kable(digit = 4, + caption = "Results for the log-likelihood estimates of the AR model. ") saveRDS(table3, file = "psi_pf_experiments/table3.rds") ``` ```{r table3, echo = FALSE} @@ -416,10 +431,10 @@ Although with fixed number of particles the $\psi$-APF produces smaller standard ```{r state1_ar, echo = FALSE, eval = FALSE} truth <- reference["alpha_1"] -sumr <- results %>% group_by(method, N) %>% +sumr <- results|> group_by(method, N)|> summarise(mean = mean(alpha_1), SD = sd(alpha_1), IRE = IRE(alpha_1, time)) -table4 <- sumr %>% arrange(N) %>% knitr::kable(digit = 4, +table4 <- sumr|> arrange(N)|> knitr::kable(digit = 4, caption = "Results for the alpha_1 estimates of the AR model. ") saveRDS(table4, file = "psi_pf_experiments/table4.rds") ``` diff --git a/vignettes/sde_model.Rmd b/vignettes/sde_model.Rmd index e6be0b8c..2fd74d7b 100644 --- a/vignettes/sde_model.Rmd +++ b/vignettes/sde_model.Rmd @@ -14,6 +14,7 @@ vignette: | --- ```{r, echo = FALSE} +Sys.setenv("OMP_NUM_THREADS" = 2) # For CRAN if (!requireNamespace("rmarkdown") || !rmarkdown::pandoc_available("1.12.3")) { warning(call. = FALSE, "These vignettes assume rmarkdown and pandoc version 1.12.3. These were not found. Older versions will not work.") @@ -82,14 +83,15 @@ Finally, we can draw our estimated state trajectory and the the corresponding 95 ```{r} suppressMessages(library("ggplot2")) suppressMessages(library("dplyr")) +suppressMessages(library("diagis")) d <- as.data.frame(out, variable = "states") -state_fit <- d %>% - group_by(time) %>% - summarise(state = mean(value), - lwr = quantile(value, 0.025), - upr = quantile(value, 0.975)) +state_fit <- d |> + group_by(time) |> + summarise(state = weighted_mean(value, weight), + lwr = weighted_quantile(value, weight, 0.025), + upr = weighted_quantile(value, weight, 0.975)) ggplot(state_fit, aes(x = time, y = state)) + geom_ribbon(aes(ymin = lwr, ymax = upr), diff --git a/vignettes/ssm_nlg_template.cpp b/vignettes/ssm_nlg_template.cpp index de257d44..2badca6d 100644 --- a/vignettes/ssm_nlg_template.cpp +++ b/vignettes/ssm_nlg_template.cpp @@ -104,7 +104,7 @@ arma::mat T_gn(const unsigned int t, const arma::vec& alpha, const arma::vec& th return Tg; } -// # log-prior pdf for theta +// log-prior pdf for theta // [[Rcpp::export]] double log_prior_pdf(const arma::vec& theta) {