Skip to content

Implement two-group ANCOM-BC and strengthen DA verification - #12

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/issue-23-native-da
Nov 7, 2026
Merged

hyperpolymath merged 1 commit into
mainfrom
feat/issue-23-native-da

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

The existing ALDEx2 implementation used natural logarithms for descriptive columns, while the reference reports log2 values. This corrects those units and input boundaries, adds an independent implementation of the original two-group ANCOM-BC model from Lin and Peddada (2020), and supplies reference adapters and stronger comparison gates.

Related to JoshuaJewell/MetaManifold-WebUI#23. This does not close that issue. Package registration and the downstream integration remain separate requirements; no URL-pinned dependency is proposed.

Validation completed locally: Julia 1.12.5 package suite, 197/197 assertions including Aqua; Agda 2.6.4.3 / standard library 2.1 type-check of the expanded entry module. The new algebraic proofs concern posterior aggregation and bias cancellation/observability under explicit assumptions, not statistical calibration or floating-point correctness.

Draft gates: complete the expanded proof gate and both pinned Bioconductor comparisons (R 4.5.0, Bioconductor 3.22, ALDEx2 1.42.0, ANCOMBC 2.12.0), verify Julia 1.10, and resolve any disagreement without widening observed-failure tolerances. The local WSL environment became unresponsive during reference setup, so CI runs these gates independently. ANCOM-BC equivalence is not yet established. See docs/ANCOMBC-SPEC.md and docs/ISSUE-23-ACCEPTANCE.md for exact scope and omissions.

Correct ALDEx2 log2 units and input boundaries. Add an independent paper-derived ANCOM-BC estimator, reference comparisons, scoped algebraic proofs and explicit release gates for MetaManifold issue 23. Draft pending full conformance and registration.
@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Caution

Review failed

The pull request is closed.

Summary

Summary by CodeRabbit

  • New Features
    • Added a native implementation of the original two-group ANCOM-BC method, with effect estimates, statistical results and structural-zero reporting.
    • Added an optional R-reference comparison for ANCOM-BC.
    • ALDEx2 now excludes all-zero features and reports them in provenance; its CLR values use base-2 logarithms.
  • Documentation
    • Added method specifications and release acceptance criteria, clarifying implementation scope, limitations and conformance requirements.
  • Testing
    • Expanded checks for both methods, including reference comparisons, input boundaries and mathematical properties.

The package adds a native implementation of the original two-group ANCOM-BC model and updates ALDEx2 validation and numerical handling. It also adds exact-rational Agda proofs and expands CI reference checks and release acceptance documentation.

Changes: Methods and verification

Layer / File(s) Summary
Shared validation and ALDEx2
src/Core/composition.jl, src/Methods/ALDEx2.jl, ext/CompositionalDARCallExt.jl, test/boundary_aldex2.jl, test/runtests.jl, docs/ALDEx2-SPEC.md
Shared validators check counts, two-group design, and Monte Carlo settings. ALDEx2 and its reference adapter exclude all-zero features, record exclusions, and use base-2 CLR values. Tests cover validation, exclusions, and log units.
Native ANCOM-BC method
src/CompositionalDA.jl, src/Methods/ANCOMBC.jl, ext/CompositionalDARCallExt.jl, test/method_ancombc.jl, docs/ANCOMBC-SPEC.md
The package adds and exports native ancombc and an optional ancombc_reference method. The native method fits the documented model and returns effect, uncertainty, test, structural-zero, and provenance fields. Tests cover calculations, options, and output behaviour.
Exact-rational proofs and checks
proofs/agda/CompositionalDA/*, proofs/agda/CompositionalDA/All.agda, proofs/tests/gate-selftest.sh, test/posterior_aggregation.jl, proofs/PROOF-STATUS.md
Agda proves a bound for sums of elementwise minima and three algebraic bias identities. Julia tests and gate mutation checks cover the corresponding properties. The proof-status text records limits of these results.
Reference conformance and release gates
.github/workflows/ci.yml, test/oracle/*, Project.toml, README.md, docs/ISSUE-23-ACCEPTANCE.md, docs/R-SUNSET.md
The CI oracle matrix selects ALDEx2 or ANCOMBC and runs its matching test. ALDEx2 comparisons cover additional fields and shared Monte Carlo instances; ANCOMBC comparisons check four result columns. The release and status documents describe acceptance conditions and outstanding conformance status.

Estimated code review effort: 4 (Complex) | ~50 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant ANCOMBCOracle
  participant ancombc
  participant ancombc_reference
  participant ANCOMBC
  ANCOMBCOracle->>ancombc: Run native method on test data
  ancombc-->>ANCOMBCOracle: Return native results
  ANCOMBCOracle->>ancombc_reference: Request reference results
  ancombc_reference->>ANCOMBC: Run through the supplied runner
  ANCOMBC-->>ancombc_reference: Return reference results
  ancombc_reference-->>ANCOMBCOracle: Return reference results
  ANCOMBCOracle->>ANCOMBCOracle: Compare feature sets and result columns
Loading
Merge Risk: Moderate · up to fea94

Numeric group labels can reverse reported effect directions, and structural-zero analyses can produce inconsistent or misleading reference results. Correct these behaviors before merging and align the sunset criterion with the ANCOM-BC thresholds.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@hyperpolymath
hyperpolymath marked this pull request as ready for review November 7, 2026 15:53
@hyperpolymath
hyperpolymath merged commit d9927bf into main Nov 7, 2026
5 of 7 checks passed
@hyperpolymath
hyperpolymath deleted the feat/issue-23-native-da branch November 7, 2026 15:53
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-07T16:00:38.708768Z fea949b Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fea949b74f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +85 to +86
input <- phyloseq::phyloseq(phyloseq::otu_table(reads, taxa_are_rows=TRUE),
phyloseq::sample_data(meta))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Install phyloseq before constructing the reference input

On a clean oracle runner, the workflow installs only ANCOMBC, but this adapter unconditionally loads phyloseq; ANCOMBC 2.12.0 declares phyloseq only under Suggests, so the normal BiocManager::install(p) call does not guarantee it is present (Bioconductor package metadata). Consequently, the new ANCOMBC oracle and the documented reference adapter fail before running a comparison unless the environment happens to contain that optional package; install it explicitly or pass the matrix and metadata directly to ancombc.

Useful? React with 👍 / 👎.

Comment thread src/Core/composition.jl
Comment on lines +37 to +41
g = string.(groups)
any(isempty ∘ strip, f) && throw(CompositionError("feature names cannot be empty"))
any(isempty ∘ strip, g) && throw(CompositionError("group labels cannot be empty"))
allunique(f) || throw(CompositionError("feature names must be unique"))
levels = sort(unique(g))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve numeric group ordering before string conversion

For numeric labels whose lexical and numeric orders differ, such as groups [2, 2, 10, 10], converting them to strings before sorting produces levels "10", "2". Both methods therefore report the opposite contrast and negate every directional effect relative to the previous numeric ordering (10 vs 2), which can invert the interpretation of an experiment. Determine group indices and order from the original labels, then stringify only the displayed or R-facing values.

Useful? React with 👍 / 👎.

end

function CompositionalDA.ancombc_reference(counts::AbstractMatrix{<:Integer}, features::AbstractVector,
groups::AbstractVector; prevalence::Real=0.1, structural_zeros::Bool=false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Match the native structural-zero default in the reference wrapper

When a taxon is absent from one group, calling ancombc and ancombc_reference with their defaults compares different analyses: the native method defaults structural_zeros=true, removes that taxon from the bias fit, and reports undefined numerical statistics, while this wrapper defaults to false and asks R to analyze it numerically. The oracle tests explicitly pass false to both calls, so they do not expose this default-path disagreement; use the same default in both public APIs.

Useful? React with 👍 / 👎.

Comment on lines +68 to +70
groups::AbstractVector; prevalence::Real=0.1, structural_zeros::Bool=false,
negative_lower_bound::Bool=false, conserve::Bool=true, tolerance::Real=1e-7,
max_iterations::Integer=1000, runner=f -> f())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward custom pseudocounts to the ANCOM-BC reference

The native API exposes pseudocount, and ANCOMBC 2.12.0 exposes the corresponding pseudo argument, but this wrapper neither accepts nor forwards it. Thus ancombc_reference(...; pseudocount=0.5) raises an unknown-keyword error, while omitting the keyword silently compares a native non-default run against R's default pseudo-count of 1; add the keyword, validate it, and pass it as pseudo=pseudocount (ANCOMBC reference manual).

Useful? React with 👍 / 👎.

Comment thread src/Methods/ANCOMBC.jl
fit = ancombc_bias(moments.difference, moments.variance; tolerance, max_iterations)
d = length(kept)
effect, se, statistic, p = (fill(NaN, d) for _ in 1:4)
effect[tested] = moments.difference .- (conserve ? fit.wls : fit.bias)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the EM bias correction when conserve is enabled

With the default conserve=true, this switches the point estimate from the fitted middle-component/EM bias (fit.bias) to the WLS approximation (fit.wls). In original ANCOM-BC, the coefficient is corrected by the EM estimate regardless of conserve; the WLS estimator is used to approximate the EM estimator's variance, and conserve changes the standard error rather than the effect (ANCOM-BC paper). This changes reported effects and can change signs and significance whenever the two bias estimates differ; always subtract fit.bias here and reserve fit.variance for the conservative SE.

Useful? React with 👍 / 👎.

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