Skip to content

feat: new Bayesian Quality Control module with Bayesian Process Capability Study - #1

Draft
juliuspfadt wants to merge 8 commits into
jasp-stats:masterfrom
juliuspfadt:feat/bayesian-process-capability
Draft

feat: new Bayesian Quality Control module with Bayesian Process Capability Study#1
juliuspfadt wants to merge 8 commits into
jasp-stats:masterfrom
juliuspfadt:feat/bayesian-process-capability

Conversation

@juliuspfadt

@juliuspfadt juliuspfadt commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Sets up jaspBayesianQualityControl and lands its first analysis, the Bayesian Process Capability Study, ported from the long-stalled jasp-stats/jaspQualityControl#414.

Splitting the Bayesian analyses into their own module is the conclusion the original PR authors reached themselves — see this thread, where @julianwuth reports that he and EJ agreed a separate module was the better home. #414 stays open until this is merged; nothing is removed from jaspQualityControl here.

Why a port rather than a merge

vandenman:bqc is merge-dirty and not rebaseable against jaspQualityControl master, and its diff carries 4166 lines of renv.lock churn, a stray example.csv, an .Rproj edit, and two roxygen man/*.Rd files left over from doeAnalysis. Porting the four source files was cleaner than untangling that.

The analysis code is committed verbatim in 7784b18 and authored to @vandenman, so the fixes that follow are reviewable as a diff against his original rather than blended into a squashed import. Only change in that commit: the dead common/Priors.qml is dropped and common/PriorsNew.qml takes its name (the analysis only ever referenced PriorsNew).

The PR did not run as-is

Every unit-test job on #414 failed at renv::restore() with a dependency-solve conflict, so CI never reached the code and none of it had ever executed. Five real bugs came out of getting it to run:

1. Broken against the current qc API. The predictive plots called three qc::: internals, and samples_to_mu_and_sigma has since been deleted from qc. The default path is unaffected — a conjugate prior under method = "integration" goes through .nig_posterior, which still exists — but any non-conjugate prior ("Informed uniform", custom informative) lands in the branch that calls the deleted function, so both predictive plots error. Verified reachable: a uniform prior resolves to method = "integration" with prior_resolved not PriorConjugate.

All three internals are replaced with exported qc::extract_predictive_samples(), which is exactly qc's own internal chain, handles method = "integration" natively, and adds a degenerate-data guard the original lacked.

⚠️ Behaviour change worth a look, @vandenman: the original conjugate branch marginalised the NIG posterior analytically to a t-distribution (per Murphy 2007); qc::extract_predictive_samples draws Monte Carlo samples from the same posterior. Same distribution, different draws — posterior-predictive plots will not be pixel-identical to your version. If you would rather keep the analytic form, the alternative is exporting .nig_posterior from qc and reinstating it.

2. Cpu and Cpl were silently dropped. qc names these metrics Cpu/Cpl and errors on CpU/CpL, but .bpcsGetSelectedMetrics built CpU/CpL. The %in% filters in the capability table, the interval table and the sequential plot therefore removed both, and qc::plot_density(what = ) would have rejected them. Ticking either box in the GUI produced nothing at all.

3. The interval-estimate plot's dependencies were the inverse of its QML flags. It declared hasEstimate/hasCi (controls that plot does not have) and omitted hasType, so the Lower and Upper fields never invalidated it. This is the issue @julianwuth reported in January; his second point, the 0–1 y-limits, had already been addressed.

4. The MCMC settings were never read. noChains/noWarmup/noIterations appeared in .bpcsStateDeps() but nowhere else, so changing them invalidated the cache and recomputed an identical fit. They are now passed to every qc::bpc call — but see the open question below, because that alone does not make them work.

5. The "Posterior updating table" checkbox does nothing. sequentialAnalysisUpdatingTable is defined in the qml (marked // TODO:) with an info tooltip describing the table it would show, but has no R implementation at all — ticking it adds no output. Left unimplemented, with a test pinning the current behaviour so it is not silently forgotten. Worth hiding the control until the table exists.

Also: getCustomAxisLimits was leaking into the package namespace (renamed .bpcsGetCustomAxisLimits); rlang was imported for a .data pronoun that is never used; stats was used via :: but undeclared; and %||% (base R only since 4.4.0) was used without an R version requirement, so Depends: R (>= 4.4.0) is now declared.

Open question: the MCMC Settings group does nothing

qc::bpc defaults to method = "integration" — deterministic numerical integration, not sampling — and there is no QML control anywhere for the estimation method. So even with fix 4 applied, chains/warmup/iterations have no effect on the capability table; they only reach the MCMC fallback inside the predictive plots.

Two ways out, and this is a call for @vandenman rather than something to guess at:

  • add a control for the estimation method (integration vs. mcmc), which makes the existing settings meaningful, or
  • drop the MCMC Settings group, since as shipped it tells users they are tuning a sampler that never runs.

Left as-is and documented in the test file rather than resolved unilaterally.

Tests

#414 shipped with none. This adds 10 tests / 26 assertions over the capability table, metric selection, the interval table, the distribution and predictive plots, the sequential analysis, readiness, and the regressions above, against a small committed dataset. Full suite runs in roughly 20 min against the locked dependency set, dominated by the sequential analysis, which refits once per observation. That is within the range of jaspQualityControl's own jobs, but say the word and the sequential test can be trimmed or skipped on CI.

Because the fit is numerical rather than sampled, results are deterministic across runs — so this suite should be far less platform-brittle than a sampler-based one would have been.

One wrinkle worth knowing: jaspTools::analysisOptions() does not expand the Common.PlotLayout / Common.Priors components, so it returns only 37 of the 148 options this analysis needs. The test file carries a helper that supplies the other 111 at their qml defaults. This is a pre-existing jaspTools limitation rather than anything new here — jaspQualityControl's own variablesChartsSubgroups has the same gap with Common.ControlChartTests.

Not in this PR

  • Module registrationjasp-desktop/Modules/modules-settings.json and a mirror repo under jasp-stats-modules, once build-bundle is green.
  • Bayesian Gauge r&R (#376) follows separately. It is not self-contained: it calls 10 internal helpers from msaGaugeRR.R and commonQualityControl.R, so it needs a companion PR exporting those from jaspQualityControl. Heads-up for whoever picks it up — .checkIfCrossed dropped its measurements argument on master, but #376 still calls it with four.

Lockfile

Regenerated from the template's 61 packages to 129, against R 4.5.2 to match CI. qc, jaspBase and jaspGraphs are pinned by commit SHA rather than branch, so the kind of silent qc API drift that broke #414 cannot recur. fs, glue and posterior had been recorded against literal repository URLs absent from the lockfile; all three are on CRAN at identical versions and were normalised. RSPM is declared alongside CRAN, mirroring jaspQualityControl. jaspTools is deliberately absent — jasp-actions installs it separately, and jaspQualityControl omits it too.

Verified: renv::status() reports the project consistent, and the suite passes under the renv-activated environment against the locked versions, not just against a hand-assembled library path.

Dependency note

qc (FBartos/qc, v0.0.0.9000) is GitHub-only and compiles Stan models (LinkingTo: BH, Rcpp, RcppEigen, RcppParallel, rstan, StanHeaders, SystemRequirements: GNU make). This was the main risk on the module, and CI has now settled it: qc builds from source and the full suite passes on windows-latest, ubuntu-latest and macos-latest (2.6 min and 2.4 min to compile on Windows and Ubuntu respectively), under both the lockfile and latest-CRAN resolutions.

Still outstanding: macOS x86_64 is not covered by unit-tests (macos-latest is arm64), and the .JASPModule bundle format itself is unproven. Both need build-bundle, which is issue_comment-triggered against base master and so cannot run until build-bundle.yml is on master.

Unrelated but noticed while testing: with degenerate specification limits (target far outside the observed range) qc returns Cpc around 1.7e+244 with sd exactly 0 rather than erroring. Looks like a qc issue rather than a module one, @FBartos / @vandenman.

🤖 Generated with Claude Code

juliuspfadt and others added 8 commits August 6, 2026 09:47
strip template example analyses, reuse quality control icons,
set module metadata and Description.qml

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ported verbatim from jasp-stats/jaspQualityControl#414 (branch vandenman:bqc,
commit afe3837). dead common/Priors.qml dropped, common/PriorsNew.qml renamed
to common/Priors.qml.
- qc:::samples_to_mu_and_sigma no longer exists; replace the three qc:::
  internals with exported qc::extract_predictive_samples, which handles the
  integration method natively (incl. degenerate data) and drops the
  hand-rolled NIG predictive
- noChains/noWarmup/noIterations were dependencies but never read, so the
  MCMC settings in the GUI had no effect; pass them to all qc::bpc calls
- interval estimate plot declared hasEstimate/hasCi deps and omitted
  hasType, the inverse of its Common.PlotLayout flags, so the Lower/Upper
  fields did not invalidate the plot (reported by julianwuth on #414)
- rename getCustomAxisLimits -> .bpcsGetCustomAxisLimits, it leaked into
  the package namespace
- drop unused rlang import

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
qc returns metrics named Cpu/Cpl and errors on CpU/CpL, but
.bpcsGetSelectedMetrics built CpU/CpL. the %in% filters in the capability
table, interval table and sequential plot therefore dropped both metrics,
and qc::plot_density(what=) would have rejected them, so ticking Cpu or
Cpl in the GUI produced nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#414 shipped with no tests. covers the capability table, metric selection,
interval table, distribution and predictive plots, readiness, and both
regressions fixed here (metric name casing, plot dependencies).

includes a helper supplying the options jaspTools cannot read, it does not
expand the Common.PlotLayout / Common.Priors qml components.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- stats:: is used for density/median/quantile but was undeclared
- %||% at the mcmc fallback is base R only since 4.4.0, undeclared
- drop qualityControl-measurement.svg, unreferenced until gauge r&R lands
- cover the sequential analysis plots, previously untested, and pin the
  unimplemented "Posterior updating table" option so it is not forgotten

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
the template lockfile carried 61 packages and none of qc, BayesTools,
HDInterval or ggh4x, so renv::restore() could not have satisfied this
module. regenerated to 129 packages against R 4.5.2, matching CI.

- qc, jaspBase and jaspGraphs are pinned by commit sha, so the qc API
  drift that broke #414 cannot recur silently
- fs, glue and posterior had been recorded against literal repository
  urls not present in the lockfile; all three are on CRAN at the same
  versions, so they are normalised to CRAN
- RSPM is declared alongside CRAN, mirroring jaspQualityControl, so the
  ggh4x entry resolves

jaspTools is deliberately absent, jasp-actions installs it separately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
the template ships unittests.yml with its push/pull_request triggers
commented out behind workflow_dispatch, so nothing ran on this PR.
enabled them, with renv.lock added to the watched paths.

build-bundle.yml is needed to prove the Stan-compiled qc dependency
bundles on windows, linux and both macOS targets, which is the open
risk on this module. test-coverage.yml skips drafts by design.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@juliuspfadt juliuspfadt closed this Aug 7, 2026
@juliuspfadt juliuspfadt reopened this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants