Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@
[submodule "submodules/FASTAN"]
path = submodules/FASTAN
url = https://github.com/thegenemyers/FASTAN.git
[submodule "submodules/minipoa"]
path = submodules/minipoa
url = https://github.com/ComparativeGenomicsToolkit/minipoa.git
branch = cactus-integration
23 changes: 22 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ modules = api setup caf bar hal reference pipeline preprocessor
# both cactus and sonLib
# jemalloc is conditionally added based on include.mk settings
submodules1 = sonLib cPecan hal matchingAndOrdering pinchesAndCacti abPOA lastz paffy red collapse-bubble FASTGA FASTAN alntools
ifeq ($(minipoa),on)
submodules1 += minipoa
endif
submodules2 = cactus2hal
submodules = ${submodules1} ${submodules2}

Expand Down Expand Up @@ -209,6 +212,14 @@ evolver_test_update_branch_local: ${CWD}/test/mammals-truth.maf
evolver_test_poa_local: all ${CWD}/test/primates-truth.maf
PYTHONPATH="${CWD}/submodules/" CACTUS_BINARIES_MODE=local CACTUS_DOCKER_MODE=0 ${PYTHON} -m pytest ${pytestOpts} -s test/evolverTest.py::TestCase::testEvolverPOALocal

# minipoa, same dataset and tolerance as evolver_test_poa_local, so the two are comparable
evolver_test_minipoa_local: all ${CWD}/test/primates-truth.maf
PYTHONPATH="${CWD}/submodules/" CACTUS_BINARIES_MODE=local CACTUS_DOCKER_MODE=0 ${PYTHON} -m pytest ${pytestOpts} -s test/evolverTest.py::TestCase::testEvolverMinipoaLocal

# the mammals head-to-head against evolver_test_local, which runs the same data through abpoa
evolver_test_minipoa_mammals_local: all ${CWD}/test/mammals-truth.maf
PYTHONPATH="${CWD}/submodules/" CACTUS_BINARIES_MODE=local CACTUS_DOCKER_MODE=0 ${PYTHON} -m pytest ${pytestOpts} -s test/evolverTest.py::TestCase::testEvolverMinipoaMammalsLocal

evolver_test_refmap_local: all ${CWD}/test/primates-truth.maf
PYTHONPATH="${CWD}/submodules/" CACTUS_BINARIES_MODE=local CACTUS_DOCKER_MODE=0 ${PYTHON} -m pytest ${pytestOpts} -s test/evolverTest.py::TestCase::testEvolverRefmapLocal

Expand Down Expand Up @@ -246,7 +257,7 @@ evolver_test_primates_pangenome_resume_local: all ${CWD}/test/primates-truth.maf
evolver_test_primates_pangenome_extend_local: all ${CWD}/test/primates-truth.maf
PYTHONPATH="${CWD}/submodules/" CACTUS_BINARIES_MODE=local CACTUS_DOCKER_MODE=0 ${PYTHON} -m pytest ${pytestOpts} -s test/evolverTest.py::TestCase::testEvolverPrimatesPangenomeExtendLocal

evolver_test_all_local: evolver_test_local evolver_test_prepare_toil evolver_test_decomposed_local evolver_test_prepare_no_outgroup_local evolver_test_poa_local evolver_test_refmap_local evolver_test_minigraph_local
evolver_test_all_local: evolver_test_local evolver_test_prepare_toil evolver_test_decomposed_local evolver_test_prepare_no_outgroup_local evolver_test_poa_local evolver_test_minipoa_local evolver_test_minipoa_mammals_local evolver_test_refmap_local evolver_test_minigraph_local

yeast_test_local:
PYTHONPATH="${CWD}/submodules/" CACTUS_BINARIES_MODE=local CACTUS_DOCKER_MODE=0 ${PYTHON} -m pytest ${pytestOpts} -s test/evolverTest.py::TestCase::testYeastPangenomeLocal
Expand Down Expand Up @@ -331,6 +342,16 @@ suball.abPOA:
ln -f submodules/abPOA/include/*.h ${INCLDIR}
rm -fr ${INCLDIR}/simde && cp -r submodules/abPOA/include/simde ${INCLDIR}

# minipoa carries a plain Makefile for this, because cactus does not drive cmake and because
# upstream's CMakeLists picks its SIMD backend by probing the build host. It reads the same
# avx2/sse41/sse2/armv8 environment variables include.mk already exports for abPOA, and produces a
# fixed archive name, so there is no need for abPOA's cascade of if-file-exists links.
# Only minipoa_c.h is published: the rest of minipoa's headers are C++ and bar is C.
suball.minipoa:
cd submodules/minipoa && ${MAKE}
ln -f submodules/minipoa/lib/libminipoa.a ${LIBDIR}/libminipoa.a
ln -f submodules/minipoa/include/minipoa_c.h ${INCLDIR}

suball.lastz: suball.jemalloc
# Inject ${LIBS} into lastz's link lines so jemalloc reaches it. This must not assume the
# link line is still pristine: makeBinRelease seds 's/-lm/-lm -static/g' into this same file
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Cactus uses many different algorithms and individual code contributions, princip
- Melissa Jane Hubiz and Adam Siepel for halPhyloP and [Phast](http://compgen.cshl.edu/phast/).
- B Gulhan, R Burhans, R Harris, M Kandemir, M Haeussler, A Nekrutenko for [KegAlign](https://github.com/galaxyproject/KegAlign), the GPU-accelerated version of LastZ.
- Yan Gao et al. for [abPOA](https://github.com/yangao07/abPOA)
- Haodong Liu et al. for [minipoa](https://github.com/NCl3-lhd/minipoa), an optional alternative base aligner in BAR: "Minipoa: A minimizer-based method for fast and memory-efficient partial order alignment" ([doi:10.64898/2026.02.18.706716](https://doi.org/10.64898/2026.02.18.706716))
- Heng Li for [minigraph](https://github.com/lh3/minigraph), [minimap2](https://github.com/lh3/minimap2), [gfatools](https://github.com/lh3/gfatools) and [dna-brnn](https://github.com/lh3/dna-rnn)
- Dany Doerr for [GFAffix](https://github.com/marschall-lab/GFAffix), used to optionally clean pangenome graphs.
- The vg team for [vg](https://github.com/vgteam/vg), used to process pangenome graphs.
Expand Down
8 changes: 8 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# Unreleased

- `minipoa` added as an optional third base aligner in BAR, selected with `<bar baseAligner="minipoa">` in the config. abPOA remains the default. The two are equivalent on accuracy (evolver primates 0.9974 against abPOA's 0.9973, mammals 0.8506 against 0.8513) and minipoa uses roughly half the memory at the same window size: on human/chimp chr10, 3.5 GB and 191 s of BAR against abPOA's 7.9 GB and 221 s. Note the win is per-window cost, not a larger window -- minipoa's memory is quadratic-ish in the window too, and 50 kb upwards is OOM-killed on that data.
- minipoa shares abPOA's substitution matrix (from `<bar><poa>`) but has its own gap penalties in `<bar><minipoa>`, because the same nominal values do not produce the same alignment. abPOA's gap model is convex, so its effective extension past ~28bp is 1, not 30; minipoa has a single affine piece and charging it 30 made long gaps ~30x dearer, costing six points of mammals accuracy. `--lastTrain` fits a *single* affine gap model, which is exactly minipoa's model, so the learned gaps are passed to minipoa verbatim; abPOA additionally gets a synthesised second piece it needs for stability, which minipoa does not.
- `<bar baseAligner>` replaces the `<bar partialOrderAlignment>` boolean as the way to choose between pecan, abpoa and minipoa. Configs with no `baseAligner` still work: the boolean is used as before.
- Fix `--lastTrain` ignoring `partialOrderAlignmentTrainedGapOpen2Factor`: `apply_long_gap` was being passed the extension factor in both the open and extension positions, so the open factor was read from the config and discarded. Both default to 3, so results with the shipped config are unchanged; setting them to different values previously did nothing (and setting the extension factor to 1 raised an assertion).
- `CACTUS_BAR_DUMP_DIR` dumps every window handed to the base aligner, with a command line that replays it. This was previously a commented-out `#define` that also deleted its own output.

# Release 3.3.0 2026-08-11

This release a few general patches and new pangenome functionality such as graph reference (gref), panacus and panpatch support.
Expand Down
41 changes: 30 additions & 11 deletions bar/impl/bar.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,13 @@ void bar(stList *flowers, CactusParams *params, CactusDisk *cactusDisk, stList *
//Parse the many, many necessary parameters from the params file
//////////////////////////////////////////////

bar_dump_dir_init(); // before any thread starts, so the flower loop never calls getenv

int64_t maximumLength = cactusParams_get_int(params, 2, "bar", "bandingLimit");
int64_t usePoa = cactusParams_get_int(params, 2, "bar", "partialOrderAlignment");
BaseAligner engine = baseAligner_constructFromCactusParams(params);
// Every site that used to ask "poa or pecan?" still only needs that much. abpoa and minipoa
// both produce an MSA and so share the AlignmentBlock/stPinch path; only pecan differs.
bool usePoa = engine != BASE_ALIGNER_PECAN;

// Pecan prams
int64_t spanningTrees = cactusParams_get_int(params, 3, "bar", "pecan", "spanningTrees");
Expand All @@ -71,14 +76,30 @@ void bar(stList *flowers, CactusParams *params, CactusDisk *cactusDisk, stList *
StateMachine *sM = stateMachine5_construct(fiveState);
bool pruneOutStubAlignments = cactusParams_get_int(params, 3, "bar", "pecan", "pruneOutStubAlignments");

// Poa params
// toggle from pecan to abpoa for multiple alignment, by setting to non-zero
// Note that poa uses about N^2 memory, so maximum value is generally in 10s of kb
int64_t poaWindow = cactusParams_get_int(params, 3, "bar", "poa", "partialOrderAlignmentWindow");
int64_t maskFilter = cactusParams_get_int(params, 3, "bar", "poa", "partialOrderAlignmentMaskFilter");
// Poa params. The window and the mask filter come from whichever engine is selected:
// Both engines are held to 10s of kb: memory is quadratic-ish in the window for either.
// minipoa's advantage is per-window cost, not a bigger window.
int64_t poaWindow, maskFilter;
if (engine == BASE_ALIGNER_MINIPOA) {
poaWindow = cactusParams_get_int(params, 3, "bar", "minipoa", "minipoaWindow");
maskFilter = cactusParams_get_int(params, 3, "bar", "minipoa", "minipoaMaskFilter");
} else {
poaWindow = cactusParams_get_int(params, 3, "bar", "poa", "partialOrderAlignmentWindow");
maskFilter = cactusParams_get_int(params, 3, "bar", "poa", "partialOrderAlignmentMaskFilter");
}
// abpoa-only progressive-mode guards; inert for minipoa, which has no such mode enabled.
int64_t poaMaxProgRows = cactusParams_get_int(params, 3, "bar", "poa", "partialOrderAlignmentProgressiveMaxRows");
double poaMaxLenDiff = cactusParams_get_float(params, 3, "bar", "poa", "partialOrderAlignmentProgressiveMaxLengthDiff");
abpoa_para_t *poaParameters = usePoa ? abpoaParamaters_constructFromCactusParams(params) : NULL;
PoaParameters *poaParameters = poaParameters_constructFromCactusParams(params, engine);

// Say which aligner ran. Without this a report of a suspect alignment cannot be tied back to
// the engine or the settings that produced it.
if (usePoa) {
st_logInfo("bar: base aligner %s, window %" PRIi64 ", maskFilter %" PRIi64 ", bandingLimit %" PRIi64 "\n",
baseAligner_toString(engine), poaWindow, maskFilter, maximumLength);
} else {
st_logInfo("bar: base aligner pecan, bandingLimit %" PRIi64 "\n", maximumLength);
}

//////////////////////////////////////////////
//Run the bar algorithm
Expand Down Expand Up @@ -160,7 +181,7 @@ void bar(stList *flowers, CactusParams *params, CactusDisk *cactusDisk, stList *
void *alignments;
if (usePoa) {
/*
* This makes a consistent set of alignments using abPoa.
* This makes a consistent set of alignments using the selected POA engine.
*
* It does not use any precomputed alignments, if they are provided they will be ignored
*/
Expand Down Expand Up @@ -284,7 +305,5 @@ void bar(stList *flowers, CactusParams *params, CactusDisk *cactusDisk, stList *
pairwiseAlignmentBandingParameters_destruct(pairwiseAlignmentParameters);
stateMachine_destruct(sM);

if (poaParameters) {
abpoa_free_para(poaParameters);
}
poaParameters_destruct(poaParameters);
}
Loading