C++ Architecture
This page has not yet passed technical or readability review.
The bgms C++ backend is organized in five top-level directories under src/. Each directory has a distinct role, and dependencies flow downward: algorithms depend on math utilities but not on models, and the execution layer orchestrates everything.
Source layout
src/
├── mcmc/
│ ├── algorithms/ # Stateless: nuts_step(), leapfrog(), metropolis_step(),
│ │ # hamiltonian_utils.h/cpp (kinetic energy, step-size heuristic)
│ ├── samplers/ # Stateful: NUTSSampler, MetropolisSampler, GibbsSampler
│ └── execution/ # Orchestration: chain_runner, warmup_schedule, chain_result
│ # + sampler_config.h (SamplerConfig: the one settings
│ # struct every chain is run from),
│ # step_result.h (per-iteration sampler return)
├── models/
│ ├── base_model.h # Abstract interface (30+ virtual methods)
│ ├── ggm/ # GGMModel — NUTS, adaptive Metropolis, or conjugate Gibbs
│ │ # + graph_constraint_structure.h (null-space bookkeeping),
│ │ # ggm_gradient.h/cpp (theta-space forward map + adjoint),
│ │ # zratio_engine.h/cpp (Z-ratio estimator),
│ │ # zratio_gauge.h (in-chain trust gauge accumulator)
│ ├── omrf/ # OMRFModel — ordinal MRF, gradient-based
│ ├── mixed/ # MixedMRFModel — mixed discrete-continuous MRF
│ └── bgmCompare/ # Group comparison sampler, helpers, logp/grad,
│ # bgmCompare_state.h (sweep state), output
├── priors/
│ ├── edge_prior.h # BaseEdgePrior + Bernoulli, BetaBernoulli, SBM
│ ├── edge_prior_correction.h # Normalizing-constant correction tables
│ ├── sbm_edge_prior.h # MFM-SBM Gibbs updates
│ ├── sbm_edge_prior.cpp
│ └── parameter_prior.h # BaseParameterPrior + Cauchy, Normal, BetaPrime, GammaScale
├── math/
│ ├── cholupdate.h/cpp # Rank-1 Cholesky update/downdate (Givens/hyperbolic)
│ ├── cholesky_helpers.h # Log-determinant, Schur complement
│ ├── log_sum_exp.h # Stable log-sum-exp
│ ├── custom_explog.h/cpp # OpenLibM exp/log for cross-platform consistency
│ ├── custom_arma_explog.h # Armadillo wrappers for custom exp/log
│ └── explog_macros.h # Platform detection macros (MY_EXP, ARMA_MY_EXP)
├── utils/
│ ├── variable_helpers.h/cpp # Numerically stable MRF probabilities
│ ├── common_helpers.h # Enums (UpdateMethod, EdgePrior), counting helpers
│ └── progress_manager.h/cpp # Multi-chain progress bar (main-thread contract)
├── rng/
│ └── rng_utils.h # SafeRNG wrapper around dqrng::xoshiro256plusplus
└── [interface files] # Rcpp entry points (sample_ggm.cpp, sample_omrf.cpp,
# sample_mixed.cpp, bgmCompare_interface.cpp) and
# *_test_interface.cpp unit-test hooks
The *_test_interface.cpp files (e.g. ggm_gibbs_test_interface.cpp, warmup_schedule_test_interface.cpp, zratio_test_interface.cpp) expose internal C++ machinery to R-level unit tests; they are not part of the user-facing API.
Layer separation
| Layer | Location | Responsibility |
|---|---|---|
| Algorithms | mcmc/algorithms/ |
Pure functions that perform one MCMC step. No state, no model knowledge. |
| Samplers | mcmc/samplers/ |
Stateful objects that own adaptation state and delegate to algorithms. |
| Execution | mcmc/execution/ |
Chain runner, warmup scheduling, sample storage. |
| Models | models/ |
Log-posterior, gradient, Metropolis proposals. Each model implements BaseModel. |
| Priors | priors/ |
Edge inclusion prior updates. Each prior implements BaseEdgePrior. |
| Math | math/ |
Numerical primitives: Cholesky updates, cross-platform exp/log. |
| Utilities | utils/ |
Shared helpers: stable probability computation, progress tracking, enums. |
Design principles
Separation of concerns. Algorithms know nothing about models. A nuts_step() call receives a function logp_and_gradient(vec) → (double, vec) and returns a StepResult. The model provides the function; the algorithm does the math.
Virtual dispatch for models. All models inherit from BaseModel and override a small set of virtual methods. The chain runner calls model.logp_and_gradient() without knowing whether it is sampling a GGM or an ordinal MRF. See Model Classes.
Cloning for parallelism. Both BaseModel and BaseEdgePrior expose a clone() method that produces an independent copy. The Rcpp entry point constructs a single model from R input and passes it to run_mcmc_sampler(). Inside C++, that prototype is cloned once per chain (each clone receiving its own RNG seed), and the clones are dispatched to TBB threads via RcppParallel::parallelFor(). R never interacts with the clones directly — parallelism is entirely C++-side. After all chains finish, the vector<ChainResult> is converted to an R list and returned. See Parallel Chains.
Header-only samplers. The sampler classes in mcmc/samplers/ are header-only. They are lightweight wrappers that hold adaptation state and call algorithm functions from mcmc/algorithms/.
Rcpp interface files. The top-level .cpp files in src/ (sample_ggm.cpp, sample_omrf.cpp, sample_mixed.cpp, bgmCompare_interface.cpp) are thin Rcpp entry points. They construct C++ objects from R lists and call run_mcmc_sampler().
Build system
src/Makevars.in (configured by configure/configure.win) sets compiler flags and links against RcppArmadillo, RcppParallel (TBB), and dqrng. The file src/sources.mk lists all .cpp translation units and is auto-generated by inst/generate_makevars_sources.R.
Never hand-edit src/sources.mk. Run Rscript inst/generate_makevars_sources.R after adding or removing .cpp files.