# Backend Reliability, Parity and Object Schemas

## Two interchangeable implementation backends, one modeling contract

The approved full-MCMC interface remains `brms` with either `rstan` or `cmdstanr`. Backend portability should preserve the model family, formula, priors, estimand and sampling contract. It should not imply identical random-number streams or identical posterior draws.

    backend_capabilities()
    #> <gp3bayes_backend_capabilities_v2>
    #>   backend brms_available backend_package_available backend_package_version
    #>     rstan           TRUE                      TRUE                  2.32.7
    #>  cmdstanr           TRUE                      TRUE                   0.9.0
    #>  external_runtime_available external_runtime_version
    #>                        TRUE                     <NA>
    #>                        TRUE                   2.39.0
    #>  ready_for_package_interface algorithm
    #>                         TRUE  sampling
    #>                         TRUE  sampling
    #>                                          model_family_scope
    #>  Bernoulli-logit and positive uncensored lognormal duration
    #>  Bernoulli-logit and positive uncensored lognormal duration
    #>  unrestricted_modeling
    #>                  FALSE
    #>                  FALSE
    validate_backend_environment("rstan")
    #> <gp3bayes_backend_environment>
    #>   Backend: rstan
    #>   Status: ready
    #>   Compiler smoke test requested: FALSE
    #>                check       status                             detail
    #>         brms_package         pass                             2.23.0
    #>      backend_package         pass                             2.32.7
    #>     external_runtime         pass managed by rstan package/toolchain
    #>  compiler_smoke_test not_assessed                      not requested
    validate_backend_environment("cmdstanr")
    #> <gp3bayes_backend_environment>
    #>   Backend: cmdstanr
    #>   Status: ready
    #>   Compiler smoke test requested: FALSE
    #>                check       status        detail
    #>         brms_package         pass        2.23.0
    #>      backend_package         pass         0.9.0
    #>     external_runtime         pass        2.39.0
    #>  compiler_smoke_test not_assessed not requested

An optional compiler smoke test can be requested explicitly and is not run in this vignette:

    validate_backend_environment("rstan", compile_test = TRUE)
    validate_backend_environment("cmdstanr", compile_test = TRUE)

## Posterior-summary parity

Parity is evaluated relative to Monte Carlo uncertainty rather than exact draw identity. The data-frame interface below makes the rule transparent and is also useful for archived summary comparisons.

    rstan_summary <- data.frame(
      variable = c("b_Intercept", "b_conditiontreatment"),
      mean = c(-0.60, 0.40),
      sd = c(0.20, 0.15),
      mcse_mean = c(0.01, 0.01)
    )

    cmdstanr_summary <- data.frame(
      variable = c("b_Intercept", "b_conditiontreatment"),
      mean = c(-0.59, 0.41),
      sd = c(0.21, 0.15),
      mcse_mean = c(0.01, 0.01)
    )

    parity <- audit_backend_parity(
      rstan_summary,
      cmdstanr_summary
    )
    parity
    #> <gp3bayes_backend_parity_audit>
    #>   Status: pass
    #>   Parameters compared: 2
    #>   Review parameters: 0
    #>   Identical draws expected: FALSE
    plot(parity)

With real fits, the same function obtains posterior summaries from each fit:

    parity <- audit_backend_parity(
      fit_rstan,
      fit_cmdstanr,
      variables = c("b_Intercept", "b_conditiontreatment")
    )

## Object-schema compatibility

A stable release also needs to know when serialized object structure changes. Schema capture records structure rather than values.

    contract <- create_model_contract(
      "binary", "selected", "participant_id",
      condition_col = "condition"
    )

    schema <- capture_gp3bayes_schema(contract)
    schema
    #> <gp3bayes_object_schema>
    #>   Object class: gp3bayes_model_contract
    #>   Recorded nodes: 41
    #>   Maximum depth: 3
    #>   Values recorded: FALSE

    validation <- validate_gp3bayes_schema(contract, schema)
    validation
    #> <gp3bayes_schema_validation>
    #>   Status: pass
    #>   Schema compatibility only: TRUE

Freezing does not write anything unless a path is explicitly provided:

    frozen_schema <- freeze_gp3bayes_schema(schema)

    schema_file <- tempfile(fileext = ".rds")
    freeze_gp3bayes_schema(frozen_schema, schema_file)
    read_gp3bayes_schema(schema_file)
    #> <gp3bayes_object_schema>
    #>   Object class: gp3bayes_model_contract
    #>   Recorded nodes: 41
    #>   Maximum depth: 3
    #>   Values recorded: FALSE
    unlink(schema_file)

A schema match is a compatibility check only. It says nothing about numerical identity, statistical adequacy, or scientific validity.