Bounded BED Glist phenotype accumulation
Source:vignettes/articles/docs-qualification-glist-bounded-accumulation.Rmd
docs-qualification-glist-bounded-accumulation.RmdHistorical evidence: these measurements retain their recorded revisions, fixtures and limitations; website rendering does not rerun scientific qualification.
Revision and scope
Measured locally on 2026-09-07: gsim 0.15.0 working-tree
implementation on main, based on
e2bac30ee0425f2eaeb5efa2f30d4e2b5f69743c (0.14.0). Entry
worktree and index were clean. This is a focused integration
qualification, not a new model, biological-data benchmark, external
HAPNEST comparison, or qualification of HAP phenotype input or
gsuite-generated Glist objects. The phenotype
contract is authoritative for current semantics, supported metadata
and memory bounds.
The old R path retained chromosome chunks, concatenated all causal
columns, imputed/standardized that matrix, and multiplied it twice when
calibrating effects. The replacement freezes statistics in capped blocks
and uses one native selected-marker scan for all traits. Full-marker
effects and truth are retained; return_genotypes = TRUE now
errors for Glist input.
Read-only implementation references were gbits
02298cbb30e5d88acc0759d608631a0559675840, gmat
e8ac1b24dc66accef83b8b6a7328edf5493769cb, and qgg
bfac8b2c388afb7ae1c88019bcfef8588f81aedb. The reused
implementation is gbits BedReader checked record I/O and
xmat_scalar, adapted privately without its external
adapters, transform/selection framework or scheduling machinery. The
existing gsim packed BED diagnostic reader now shares that reader. Notices retain gbits MIT, gmat and
HAPNEST provenance.
Parity and failure checks
Six results and post-call RNG states were frozen from the entry R sources before replacement, using installed qgg 1.1.6. The independent fixture encoder creates two 32-sample, 12-marker BED files with allele-asymmetric 0/1/2 dosage, missing values, invariant and all-missing columns. Sixteen selected samples are reordered; eight eligible markers are noncontiguous. Cases cover BayesR one/two traits, BayesC non-unit weights without calibration, MAF-dependent effects, derived weights using Glist MAF, and fixed effects without standardization. Baseline comparisons included optional summary statistics.
Causal IDs, components, probability surfaces, variance weights and post-call R RNG states matched exactly. The largest absolute difference across compared B/B_causal, G, E, Y, variance/correlation quantities and residual covariance was 1.77635683940025e-15. All comparisons passed 1e-12 tolerance, appropriate for small scalar versus BLAS sums and subsequent calibration. This is not a promise of bitwise products or an error bound for arbitrary large/cancelling sums. Returned B is calibrated when requested; raw effect identity was separately verified with calibration disabled, not inferred from rounded rescaled effects.
Focused tests independently encode small BEDs and compare to the retained in-memory W path. They cover:
- One/two traits, covariance, fixed effects, explicit and derived variance weights, exact raw B, causal/component identity and RNG, block sizes 1/3/64.
- Selected-sample imputation/SD, monomorphic/all-missing behavior, sample order, noncontiguous markers, filtered BIM mappings and different per-file FAM order.
- Native registration/use without loading qgg; rejected unsupported storage, bad allele metadata, absent/duplicate IDs, invalid native indices, >64-column native requests, non-SNP-major mode and truncated BED.
- Deterministic bounded custom callbacks and bounded optional summary statistics. A 130-marker/two-trait fixture checks exact legacy summary row identity across the private decode cap, alongside numerical agreement.
The native tests pass without qgg in loaded namespaces. The existing phenotype regression includes the real installed qgg PLINK fixture (489 samples, 2,000 markers), so support is not based solely on a hand-built descriptor. The focused end-to-end phenotype regression and BED sink tests pass too. BED sink verification was included because its private diagnostic reader now shares checked I/O. No full test suite, R CMD check, large simulation or external data download ran.
One bounded resource probe
Reproduction: install into an isolated library using repository instructions, explicitly load that installed gsim, then run from the repository root:
source("tools/qualification/glist_streaming.R")
result <- glist_streaming()The script independently writes one synthetic BED record at a time. Fixed dimensions: 1,024 samples, 4,096 eligible markers, three traits, block size 32. Causal probabilities select 64/512/4,096 markers exactly. Generation seed 9107, simulation seed 552. No summary statistics in this resource probe; their cap and numerical behavior are tested separately. The synthetic dosages do not represent real 1000G LD.
Environment: Windows NT 10.0.26200.0, Intel Core i7-1365U, 12 logical processors, R 4.4.1 x64, Rtools44 GCC/G++ 13.2.0, default local R BLAS. Scalar accumulation has no new threads or scheduling. Timings are single observations with Rprofmem active, no repeated trials, cache control or confidence intervals; they are integration resource evidence, not throughput estimates.
| Causal markers | Elapsed seconds | Avoided full causal payload | Returned B_causal | Total returned object |
|---|---|---|---|---|
| 64 | 0.14 | 524,288 B | 6,320 B | 2,572,240 B |
| 512 | 0.28 | 4,194,304 B | 45,744 B | 2,722,768 B |
| 4,096 | 2.41 | 33,554,432 B | 361,136 B | 3,926,992 B |
For every run, decoded-block capacity was 262,144 bytes (256 KiB), native packed record storage 256 bytes, and the largest Rprofmem allocation 262,192 bytes (including its allocation overhead). No full causal genotype allocation appeared. Native code inspection confirms that multiplication allocates only an n ? traits R result and one packed record; the registered decode boundary rejects more than 64 columns. Rprofmem does not observe C++ heap allocations, hence both checks matter. Several bounded decoded/copy matrices may coexist; 256 KiB is not a claim about total genotype-stage peak memory.
Full B occupied 361,136 bytes in each run; G/E/Y together 272,400 bytes; input Glist metadata 329,392 bytes. These object sizes include names and R overhead, can double-count shared strings, and exclude transient R copies and the internal BIM/FAM plan. The observed whole-process peak working set was 130,052,096 bytes (124.027 MiB), sampled every 100 ms across the single process containing fixture generation and all three calls. Sampling can miss peaks; process memory also includes R, package loading, allocator retention and profiling. It is not a per-kernel allocation measurement. Generated fixtures and profiling/sampling artifacts were temporary; reusable test fixtures and the bounded probe are retained.
Standalone build and integration
One clean native build used R CMD INSTALL --preclean
into an empty temporary library outside the repository. To correct
legacy summary row order after the initial checks, R sources/help were
refreshed in the same library; native make reported nothing to rebuild.
A --no-libs refresh attempt could not stage the DLL and
restored the prior installation; the normal refresh succeeded. Explicit
isolated loading and registered native symbols were verified. DLL
imports contain only Windows runtime libraries and R.dll: no
gbits/gmat/qgg shared-library dependency, external ABI discovery or
configured sibling paths. DESCRIPTION keeps qgg in Suggests and adds no
dependency. Startup locale warnings and the installed testthat
build-version notice were environmental; test cases reported no
warnings, failures or skips in the final focused runs.
Public Rd was regenerated through roxygen2 from R sources. Website source preparation continues to consume the authoritative Markdown/Rd; no deployment or site publication is part of this milestone. Historical performance reports, scientific contracts, single-threaded pedigree meiosis and deferred external HAPNEST comparison retain their separate scope.
Implementation SHA-256 (working-tree bytes):
| File | SHA-256 |
|---|---|
R/gsim.R |
83c2206144353b70657191b4a3e00ef6567afb4b7d008551f2516e61fcc7245c |
R/gsim_internal.R |
6921879e8229e843c4f9ed5dfcaaf2842a701623a771d6f4c2f49ddfebfbf19d |
R/glist_bed_internal.R |
0af31ca8f14746fcf066d177a6dde20e6903badcdba67e5013b250c02c87ec43 |
src/bed_reader.h |
246d31f1aefbca2d0516c50a2018ed2758f5bc951181726b6b256f22d89c0475 |
src/bed_reader.cpp |
0b2574f5bd45c533b58ecee2ae3e8f966e20782a4d21ecd8ea0026299660f24d |
src/bed_phenotype_r.cpp |
aed994ab8cef572bfb76eda6adcc227f477f1498fdb469297d7fabc6dca73bb8 |