From 49225030ca8fee0cf5fa7fe2ca18da45e6916efb Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Tue, 3 Nov 2026 06:09:06 +1100 Subject: [PATCH] Implement core clustering logic and scoring features This update introduces the core clustering algorithm, including parallel execution, dynamic block sizing, and SIMD-accelerated scoring methods. It also adds robust sequence preparation, k-mer analysis, and comprehensive test data fixtures for validation. --- cmd/obitools/obiclust/main.go | 28 ++ obitests/obitools/obiclust/test.sh | 188 ++++++++ pkg/obialign/lcsband.go | 26 +- pkg/obialign/lcsband_nosimd.go | 4 + pkg/obialign/lcsband_test.go | 72 +++ pkg/obialign/lcsmin.go | 102 ++++ pkg/obialign/lcsscore_simd.go | 156 +++++++ pkg/obitools/obiclust/block.go | 46 ++ pkg/obitools/obiclust/block_nosimd.go | 10 + pkg/obitools/obiclust/block_simd.go | 25 + pkg/obitools/obiclust/centers.go | 136 ++++++ pkg/obitools/obiclust/cluster.go | 202 ++++++++ pkg/obitools/obiclust/kmer4.go | 47 ++ pkg/obitools/obiclust/kmer4_nosimd.go | 14 + pkg/obitools/obiclust/kmer4_simd.go | 35 ++ pkg/obitools/obiclust/kmer4_test.go | 71 +++ pkg/obitools/obiclust/obiclust.go | 224 +++++++++ pkg/obitools/obiclust/obiclust_test.go | 434 ++++++++++++++++++ pkg/obitools/obiclust/options.go | 246 +++++----- pkg/obitools/obiclust/output.go | 128 ++++++ pkg/obitools/obiclust/prepare.go | 178 +++++++ pkg/obitools/obiclust/score.go | 233 ++++++++++ pkg/obitools/obiclust/testdata/cases.txt | 9 + .../testdata/families_ascending.expected | 101 ++++ .../testdata/families_default.expected | 101 ++++ .../testdata/families_distance.expected | 101 ++++ .../obiclust/testdata/families_exact.expected | 101 ++++ .../testdata/families_longest.expected | 101 ++++ .../obiclust/testdata/families_ratio.expected | 101 ++++ .../testdata/families_rawdist.expected | 101 ++++ .../testdata/families_rawlcs.expected | 101 ++++ .../testdata/families_shortest.expected | 101 ++++ 32 files changed, 3404 insertions(+), 119 deletions(-) create mode 100644 cmd/obitools/obiclust/main.go create mode 100755 obitests/obitools/obiclust/test.sh create mode 100644 pkg/obialign/lcsmin.go create mode 100644 pkg/obialign/lcsscore_simd.go create mode 100644 pkg/obitools/obiclust/block.go create mode 100644 pkg/obitools/obiclust/block_nosimd.go create mode 100644 pkg/obitools/obiclust/block_simd.go create mode 100644 pkg/obitools/obiclust/centers.go create mode 100644 pkg/obitools/obiclust/cluster.go create mode 100644 pkg/obitools/obiclust/kmer4.go create mode 100644 pkg/obitools/obiclust/kmer4_nosimd.go create mode 100644 pkg/obitools/obiclust/kmer4_simd.go create mode 100644 pkg/obitools/obiclust/kmer4_test.go create mode 100644 pkg/obitools/obiclust/obiclust_test.go create mode 100644 pkg/obitools/obiclust/output.go create mode 100644 pkg/obitools/obiclust/prepare.go create mode 100644 pkg/obitools/obiclust/score.go create mode 100644 pkg/obitools/obiclust/testdata/cases.txt create mode 100644 pkg/obitools/obiclust/testdata/families_ascending.expected create mode 100644 pkg/obitools/obiclust/testdata/families_default.expected create mode 100644 pkg/obitools/obiclust/testdata/families_distance.expected create mode 100644 pkg/obitools/obiclust/testdata/families_exact.expected create mode 100644 pkg/obitools/obiclust/testdata/families_longest.expected create mode 100644 pkg/obitools/obiclust/testdata/families_ratio.expected create mode 100644 pkg/obitools/obiclust/testdata/families_rawdist.expected create mode 100644 pkg/obitools/obiclust/testdata/families_rawlcs.expected create mode 100644 pkg/obitools/obiclust/testdata/families_shortest.expected diff --git a/cmd/obitools/obiclust/main.go b/cmd/obitools/obiclust/main.go new file mode 100644 index 0000000..65b65cc --- /dev/null +++ b/cmd/obitools/obiclust/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "os" + + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obioptions" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obitools/obiclust" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obitools/obiconvert" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiutils" +) + +func main() { + optionParser := obioptions.GenerateOptionParser( + "obiclust", + "star clustering of sequences (the algorithm of sumaclust, UCLUST and CD-HIT)", + obiclust.OptionSet) + + _, args := optionParser(os.Args) + + fs, err := obiconvert.CLIReadBioSequences(args...) + obiconvert.OpenSequenceDataErrorMessage(args, err) + + clustered := obiclust.CLIOBIClust(fs) + + obiconvert.CLIWriteBioSequences(clustered, true) + + obiutils.WaitForLastPipe() +} diff --git a/obitests/obitools/obiclust/test.sh b/obitests/obitools/obiclust/test.sh new file mode 100755 index 0000000..f9277a0 --- /dev/null +++ b/obitests/obitools/obiclust/test.sh @@ -0,0 +1,188 @@ +#!/bin/bash + +# +# Here give the name of the test serie +# + +TEST_NAME=obiclust +CMD=obiclust + +###### +# +# Some variable and function definitions: please don't change them +# +###### +TEST_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" +OBITOOLS_DIR="${TEST_DIR/obitest*/}build" +export PATH="${OBITOOLS_DIR}:${PATH}" + +MCMD="$(echo "${CMD:0:4}" | tr '[:lower:]' '[:upper:]')$(echo "${CMD:4}" | tr '[:upper:]' '[:lower:]')" + +TMPDIR="$(mktemp -d)" +ntest=0 +success=0 +failed=0 + +cleanup() { + echo "========================================" 1>&2 + echo "## Results of the $TEST_NAME tests:" 1>&2 + + echo 1>&2 + echo "- $ntest tests run" 1>&2 + echo "- $success successfully completed" 1>&2 + echo "- $failed failed tests" 1>&2 + echo 1>&2 + echo "Cleaning up the temporary directory..." 1>&2 + echo 1>&2 + echo "========================================" 1>&2 + + rm -rf "$TMPDIR" # Suppress the temporary directory + + if [ $failed -gt 0 ]; then + log "$TEST_NAME tests failed" + log + log + exit 1 + fi + + log + log + + exit 0 +} + +log() { + echo -e "[$TEST_NAME @ $(date)] $*" 1>&2 +} + +log "Testing $TEST_NAME..." +log "Test directory is $TEST_DIR" +log "obitools directory is $OBITOOLS_DIR" +log "Temporary directory is $TMPDIR" +log "files: $(find $TEST_DIR | awk -F'/' '{print $NF}' | tail -n +2)" + +###################################################################### +#### +#### Below are the tests +#### +#### Before each test : +#### - increment the variable ntest +#### +#### Run the command as the condition of an if / then /else +#### - The command must return 0 on success +#### - The command must return an exit code different from 0 on failure +#### - The datafiles are stored in the same directory than the test script +#### - The test script directory is stored in the TEST_DIR variable +#### - If result files have to be produced they must be stored +#### in the temporary directory (TMPDIR variable) +#### +#### then clause is executed on success of the command +#### - Write a success message using the log function +#### - increment the variable success +#### +#### else clause is executed on failure of the command +#### - Write a failure message using the log function +#### - increment the variable failed +#### +###################################################################### + + + +((ntest++)) +if $CMD -h > "${TMPDIR}/help.txt" 2>&1 +then + log "$MCMD: printing help OK" + ((success++)) +else + log "$MCMD: printing help failed" + ((failed++)) +fi + + +# +# Dataset used by the following tests: two families with a rare variant each, +# a duplicate, and a sequence with an ambiguous nucleotide that is discarded. +# +cat > "${TMPDIR}/data.fasta" <a1 count=20; +acgtacgtacgtaacctagcctagcctagggatcgatcgatcgatcgaatcg +>a2 count=3; +acgtacgtacgtaacctagcctagcctagggatcgatcgatcgttcgaatcg +>a3 count=2; +acgtacgtacgtaacctagcctagcctagggatcgatcgatcgatcgaatcg +>b1 count=15; +ttgcatgcatgctagctagctagcatgcatcgatcgatgctagctagtagct +>b2 count=1; +ttgcatgcatgctagctagctagcatgcatcgatcgatgctagctagtaact +>x1 count=4; +ttgcatgcatgctangctagctagcatgcatcgatcgatgctagctagtagc +DATA + +((ntest++)) +if $CMD --max-cpu 2 -O -t 0.95 "${TMPDIR}/data.fasta" > "${TMPDIR}/clusters.fasta" 2> "${TMPDIR}/clusters.log" +then + log "$MCMD: clustering runs OK" + ((success++)) +else + log "$MCMD: clustering failed" + ((failed++)) +fi + +((ntest++)) +if [ "$(grep -c '^>' "${TMPDIR}/clusters.fasta")" = "5" ] +then + log "$MCMD: the sequence with an ambiguous nucleotide is discarded OK" + ((success++)) +else + log "$MCMD: wrong number of sequences in the output" + ((failed++)) +fi + +((ntest++)) +if [ "$(grep '^>a[23] ' "${TMPDIR}/clusters.fasta" | grep -c 'cluster=a1;')" = "2" ] && + [ "$(grep '^>b2 ' "${TMPDIR}/clusters.fasta" | grep -c 'cluster=b1;')" = "1" ] && + grep '^>b1 ' "${TMPDIR}/clusters.fasta" | grep -q 'cluster_center=true;' && + grep '^>a1 ' "${TMPDIR}/clusters.fasta" | grep -q 'cluster_weight=25;' +then + log "$MCMD: sequences are attached to the right center OK" + ((success++)) +else + log "$MCMD: sequences are not attached to the right center" + ((failed++)) +fi + +((ntest++)) +if $CMD --max-cpu 2 -t 0.95 --otu-map "${TMPDIR}/otu.csv" --no-sequence-output "${TMPDIR}/data.fasta" > "${TMPDIR}/none.fasta" 2> /dev/null && + [ ! -s "${TMPDIR}/none.fasta" ] && + [ "$(head -n 1 "${TMPDIR}/otu.csv")" = "id,cluster,cluster_center,cluster_score,count" ] && + [ "$(sed -n 2,4p "${TMPDIR}/otu.csv" | cut -d, -f1,2 | tr '\n' ' ')" = "a1,a1 a3,a1 a2,a1 " ] && + [ "$(grep -c ',true,' "${TMPDIR}/otu.csv")" = "2" ] +then + log "$MCMD: CSV table of the clusters OK" + ((success++)) +else + log "$MCMD: CSV table of the clusters failed" + ((failed++)) +fi + +((ntest++)) +if $CMD --max-cpu 1 -O -t 0.95 "${TMPDIR}/data.fasta" 2> /dev/null | grep '^>' | sed 's/ .*//' > "${TMPDIR}/order1.txt" && + $CMD --max-cpu 8 -O -t 0.95 "${TMPDIR}/data.fasta" 2> /dev/null | grep '^>' | sed 's/ .*//' > "${TMPDIR}/order8.txt" && + cmp -s "${TMPDIR}/order1.txt" "${TMPDIR}/order8.txt" +then + log "$MCMD: the result does not depend on the number of threads OK" + ((success++)) +else + log "$MCMD: the result depends on the number of threads" + ((failed++)) +fi + + +######################################### +# +# At the end of the tests +# the cleanup function is called +# +######################################### + +cleanup diff --git a/pkg/obialign/lcsband.go b/pkg/obialign/lcsband.go index 3c0c095..1ab8692 100644 --- a/pkg/obialign/lcsband.go +++ b/pkg/obialign/lcsband.go @@ -40,7 +40,10 @@ const ( type bandedLCSWorkspace struct { aRev, bCode []uint32 // encoded bases (aRev is reversed), SIMD kernel only rows [3][]uint32 // antidiagonal (SIMD) or row (scalar) buffers - widened int // number of alignments that needed a second pass + aRev16 []uint16 // 16-bit encoded bases of the score-only kernel + bCode16 []uint16 + rows16 [3][]uint16 + widened int // number of alignments that needed a second pass } var bandedLCSPool = sync.Pool{New: func() any { return new(bandedLCSWorkspace) }} @@ -146,13 +149,7 @@ func bandedLCS(a, b []byte, maxError int, ws *bandedLCSWorkspace, useSIMD bool) } run := func(g int) (int, int) { kmin, kmax := bandOffsets(lA, lB, g) - var key uint32 - if simdOK { - key = bandedLCSSIMD(lA, lB, kmin, kmax, ws) - } else { - key = bandedLCSScalar(a, b, kmin, kmax, ws) - } - return int(key >> 16), int(lcsLenBase - key&0xFFFF) + return ws.align(a, b, kmin, kmax, simdOK) } if maxError < 0 { @@ -173,3 +170,16 @@ func bandedLCS(a, b []byte, maxError int, ws *bandedLCSWorkspace, useSIMD bool) } return lcs, alilen } + +// align runs the banded DP on the offsets [kmin, kmax] (kmin <= 0 <= kmax) and +// returns the best (lcs, alilen) of the band. The bases must have been encoded +// with encodeBases when simdOK is set. +func (ws *bandedLCSWorkspace) align(a, b []byte, kmin, kmax int, simdOK bool) (int, int) { + var key uint32 + if simdOK { + key = bandedLCSSIMD(len(a), len(b), kmin, kmax, ws) + } else { + key = bandedLCSScalar(a, b, kmin, kmax, ws) + } + return int(key >> 16), int(lcsLenBase - key&0xFFFF) +} diff --git a/pkg/obialign/lcsband_nosimd.go b/pkg/obialign/lcsband_nosimd.go index 818a5e2..bf4b10b 100644 --- a/pkg/obialign/lcsband_nosimd.go +++ b/pkg/obialign/lcsband_nosimd.go @@ -11,3 +11,7 @@ func encodeBases(ws *bandedLCSWorkspace, a, b []byte) {} func bandedLCSSIMD(lA, lB, kmin, kmax int, ws *bandedLCSWorkspace) uint32 { return 0 } + +func lcsScoreSIMD(a, b []byte, kmin, kmax, minLCS int, ws *bandedLCSWorkspace) int { + return -1 +} diff --git a/pkg/obialign/lcsband_test.go b/pkg/obialign/lcsband_test.go index f957717..9869d5e 100644 --- a/pkg/obialign/lcsband_test.go +++ b/pkg/obialign/lcsband_test.go @@ -286,3 +286,75 @@ func TestFastLCSScoreWrapper(t *testing.T) { t.Errorf("got (%d,%d), want (-1,-1)", s, l) } } + +// TestBandedLCSAtLeast checks the exactness of the bound expressed on the +// score: the optimum when it reaches minLCS, (-1,-1) otherwise. +func TestBandedLCSAtLeast(t *testing.T) { + rng := rand.New(rand.NewSource(13)) + var buf BandedLCSBuffer + for _, alpha := range testAlphabets { + for it := 0; it < 1500; it++ { + a := randomFrom(rng, alpha.letters, 1+rng.Intn(100)) + b := mutateFrom(rng, alpha.letters, a, rng.Float64()*0.5) + if len(b) == 0 { + continue + } + os, ol := fullDP(a, b) + for _, minLCS := range []int{-3, 0, 1, os - 1, os, os + 1, len(b) / 2, len(b), len(b) + 1} { + ws, wl := os, ol + if os < minLCS { + ws, wl = -1, -1 + } + for _, got := range [][2]int{func() [2]int { s, l, _ := buf.AtLeast(a, b, minLCS); return [2]int{s, l} }(), + func() [2]int { s, l, _ := BandedLCSAtLeast(b, a, minLCS); return [2]int{s, l} }()} { + if got[0] != ws || got[1] != wl { + t.Fatalf("%s minLCS=%d a=%q b=%q: got (%d,%d), want (%d,%d)", + alpha.name, minLCS, a, b, got[0], got[1], ws, wl) + } + } + } + } + } + if _, _, err := buf.AtLeast(make([]byte, MaxBandedLCSLength+1), []byte("a"), 0); err != ErrSequenceTooLong { + t.Errorf("too long: err = %v", err) + } +} + +// TestBandedLCSScoreAtLeast checks the score-only entry point, with sequences +// long enough for the early stop of the kernel to be exercised. +func TestBandedLCSScoreAtLeast(t *testing.T) { + rng := rand.New(rand.NewSource(17)) + var buf BandedLCSBuffer + check := func(name string, a, b []byte) { + os, _ := fullDP(a, b) + for _, minLCS := range []int{-1, 0, os - 3, os - 1, os, os + 1, os + 5, len(b) - 1, len(b), (len(b) * 9) / 10, len(b) + 1} { + want := os + if os < minLCS { + want = -1 + } + got, err := buf.ScoreAtLeast(a, b, minLCS) + if err != nil || got != want { + t.Fatalf("%s minLCS=%d a=%q b=%q: got %d (%v), want %d", name, minLCS, a, b, got, err, want) + } + } + } + for _, alpha := range testAlphabets { + for it := 0; it < 800; it++ { + a := randomFrom(rng, alpha.letters, 1+rng.Intn(60)) + b := mutateFrom(rng, alpha.letters, a, rng.Float64()*0.6) + if len(b) > 0 { + check(alpha.name, a, b) + } + } + } + for it := 0; it < 150; it++ { + a := randomFrom(rng, "ACGT", 100+rng.Intn(400)) + b := mutateFrom(rng, "ACGT", a, rng.Float64()*0.3) + if it%3 == 0 { + b = randomFrom(rng, "ACGT", 50+rng.Intn(300)) + } + if len(b) > 0 { + check("long", a, b) + } + } +} diff --git a/pkg/obialign/lcsmin.go b/pkg/obialign/lcsmin.go new file mode 100644 index 0000000..93f00f9 --- /dev/null +++ b/pkg/obialign/lcsmin.go @@ -0,0 +1,102 @@ +package obialign + +// BandedLCSBuffer is a working memory that can be reused from one alignment to +// the next to avoid any allocation. It must not be shared between goroutines. +type BandedLCSBuffer struct { + ws bandedLCSWorkspace +} + +// AtLeast is BandedLCSAtLeast on the buffer. +func (b *BandedLCSBuffer) AtLeast(seqA, seqB []byte, minLCS int) (lcs, alilen int, err error) { + return atLeast(seqA, seqB, minLCS, &b.ws) +} + +func atLeast(seqA, seqB []byte, minLCS int, ws *bandedLCSWorkspace) (lcs, alilen int, err error) { + if len(seqA) > MaxBandedLCSLength || len(seqB) > MaxBandedLCSLength { + return -1, -1, ErrSequenceTooLong + } + if len(seqA) < len(seqB) { + seqA, seqB = seqB, seqA + } + lcs, alilen = bandedLCSAtLeast(seqA, seqB, minLCS, ws, haveSIMD) + return lcs, alilen, nil +} + +// BandedLCSAtLeast computes the LCS score of two nucleotide sequences and the +// length of the shortest alignment achieving it, as BandedLCS does, but with +// the bound expressed on the score: the result is the exact optimum when its +// score is at least minLCS, and (-1, -1) otherwise. +// +// The DP only visits the diagonals an alignment of score minLCS can use (one +// whose path makes at most lB-minLCS gaps on one side and lA-minLCS on the +// other), so the cost decreases as minLCS grows. This is exact: every alignment +// of score >= minLCS lies in that band, hence so does the optimum when it +// reaches minLCS, and when the best path of the band is below minLCS so is +// the global optimum. +func BandedLCSAtLeast(seqA, seqB []byte, minLCS int) (lcs, alilen int, err error) { + ws := bandedLCSPool.Get().(*bandedLCSWorkspace) + lcs, alilen, err = atLeast(seqA, seqB, minLCS, ws) + bandedLCSPool.Put(ws) + return lcs, alilen, err +} + +// bandedLCSAtLeast expects len(a) >= len(b), both within MaxBandedLCSLength. +func bandedLCSAtLeast(a, b []byte, minLCS int, ws *bandedLCSWorkspace, useSIMD bool) (int, int) { + lA, lB := len(a), len(b) + minLCS = max(minLCS, 0) + if minLCS > lB { + return -1, -1 + } + if lB == 0 { + return 0, lA + } + simdOK := useSIMD && allRegularIUPAC(a) && allRegularIUPAC(b) + kmin, kmax := -(lB - minLCS), lA-minLCS + + if !simdOK { + lcs, alilen := ws.align(a, b, kmin, kmax, false) + if lcs < minLCS { + return -1, -1 + } + return lcs, alilen + } + + // The score alone is much cheaper than the score with the length of the + // alignment: the latter is only computed when the pair reaches minLCS. + if lcsScoreSIMD(a, b, kmin, kmax, minLCS, ws) < minLCS { + return -1, -1 + } + encodeBases(ws, a, b) + return ws.align(a, b, kmin, kmax, true) +} + +// ScoreAtLeast computes the LCS score of two nucleotide sequences when it is +// at least minLCS, and returns -1 otherwise. It is much cheaper than AtLeast, +// which also computes the length of the shortest alignment. +func (b *BandedLCSBuffer) ScoreAtLeast(seqA, seqB []byte, minLCS int) (int, error) { + if len(seqA) > MaxBandedLCSLength || len(seqB) > MaxBandedLCSLength { + return -1, ErrSequenceTooLong + } + if len(seqA) < len(seqB) { + seqA, seqB = seqB, seqA + } + lA, lB := len(seqA), len(seqB) + minLCS = max(minLCS, 0) + if minLCS > lB { + return -1, nil + } + if lB == 0 { + return 0, nil + } + kmin, kmax := -(lB - minLCS), lA-minLCS + var lcs int + if haveSIMD && allRegularIUPAC(seqA) && allRegularIUPAC(seqB) { + lcs = lcsScoreSIMD(seqA, seqB, kmin, kmax, minLCS, &b.ws) + } else { + lcs, _ = b.ws.align(seqA, seqB, kmin, kmax, false) + } + if lcs < minLCS { + return -1, nil + } + return lcs, nil +} diff --git a/pkg/obialign/lcsscore_simd.go b/pkg/obialign/lcsscore_simd.go new file mode 100644 index 0000000..ac4250e --- /dev/null +++ b/pkg/obialign/lcsscore_simd.go @@ -0,0 +1,156 @@ +//go:build goexperiment.simd + +package obialign + +import "simd" + +func simdLanes16() int { + var probe simd.Uint16s + return probe.Len() +} + +func growUint16(s []uint16, n int) []uint16 { + if cap(s) < n { + return make([]uint16, n) + } + return s[:n] +} + +func encodeIUPAC16(dst []uint16, s []byte, rev bool) { + n := len(s) + clear(dst) + if rev { + for x, c := range s { + dst[n-1-x] = uint16(iupacMask[c]) + } + return + } + for x, c := range s { + dst[x] = uint16(iupacMask[c]) + } +} + +// pruneEvery is the number of antidiagonals between two tests of the upper +// bound that stops a hopeless alignment. +const pruneEvery = 8 + +// lcsScoreSIMD runs the same banded DP as bandedLCSSIMD on the score only, +// with 16-bit lanes (twice the cells per instruction): +// +// cell(i,j) = max(diag + match, up, left) +// +// A cell outside the band is 0, which never beats a real cell. The score of a +// path ending at the cell (i, j) of an antidiagonal cannot exceed the score of +// the cell plus min(lB-i, lA-j), the characters left to pair. Every path +// crosses each antidiagonal inside the window, so when the best score of the +// window plus the largest of these remainders is under minLCS the final score +// is under minLCS: -1 is returned without computing the rest of the matrix. +// +// Requires lA >= lB >= 1, kmin <= 0 <= kmax, and bases with non-zero masks. +// Returns the best score of the band, or -1 if it is proven to be under +// minLCS. +func lcsScoreSIMD(a, b []byte, kmin, kmax, minLCS int, ws *bandedLCSWorkspace) int { + lA, lB := len(a), len(b) + vl := simdLanes16() + + ws.aRev16 = growUint16(ws.aRev16, lA+vl) + ws.bCode16 = growUint16(ws.bCode16, lB+vl) + encodeIUPAC16(ws.aRev16, a, true) + encodeIUPAC16(ws.bCode16, b, false) + aRev, bCode := ws.aRev16, ws.bCode16 + + size := lB + 2*vl + 2 + for r := range ws.rows16 { + ws.rows16[r] = growUint16(ws.rows16[r], size) + clear(ws.rows16[r]) + } + p2, p1, cur := ws.rows16[0], ws.rows16[1], ws.rows16[2] + + // the first row and the first column hold a score of 0: nothing to set + + zero := simd.BroadcastUint16s(0) + one := simd.BroadcastUint16s(1) + + for t := 2; t <= lA+lB; t++ { + lo := max(1, t-lA, (t-kmax+1)>>1) + hi := min(lB, t-1, (t-kmin)>>1) + + for i := lo; i <= hi; i += vl { + ca := simd.LoadUint16s(aRev[lA-t+i:]) + cb := simd.LoadUint16s(bCode[i-1:]) + inc := one.Masked(ca.And(cb).NotEqual(zero)) + d := simd.LoadUint16s(p2[i-1:]).Add(inc) + u := simd.LoadUint16s(p1[i-1:]) + l := simd.LoadUint16s(p1[i:]) + d.Max(u).Max(l).Store(cur[i:]) + } + + zero.Store(cur[hi+1:]) + if lo > 1 { + cur[lo-1] = 0 + } + + // A diagonal move skips an antidiagonal, but a path always visits + // one of two consecutive antidiagonals. + if t%pruneEvery == 0 && minLCS > 0 && + scoreBound(p1, t-1, lA, lB, kmin, kmax) < minLCS && + scoreBound(cur, t, lA, lB, kmin, kmax) < minLCS { + return -1 + } + + p2, p1, cur = p1, cur, p2 + } + return int(p1[lB]) +} + +// iota16 is 0, 1, 2... to build the row index of the lanes. +var iota16 = func() (t [64]uint16) { + for i := range t { + t[i] = uint16(i) + } + return t +}() + +// scoreBound is an upper bound of the final score of the paths going through +// the antidiagonal t, whose scores are in row. A path through the cell (i, j) +// scores at most the score of the cell plus min(lB-i, lA-j), the characters +// left to pair, so the bound is the maximum of this sum on the cells of the +// window. The sum is computed by vectors on the full chunks of the window. +func scoreBound(row []uint16, t, lA, lB, kmin, kmax int) int { + lo := max(1, t-lA, (t-kmax+1)>>1) + hi := min(lB, t-1, (t-kmin)>>1) + + bound := 0 + i := lo + if hi-lo >= simdLanes16() { + vl := simdLanes16() + idx := simd.LoadUint16s(iota16[:]).Add(simd.BroadcastUint16s(uint16(lo))) + step := simd.BroadcastUint16s(uint16(vl)) + remB := simd.BroadcastUint16s(uint16(lB)) + // lA-t+i is not negative on the window; the 16-bit arithmetic is modular + offA := simd.BroadcastUint16s(uint16(lA - t)) + acc := simd.BroadcastUint16s(0) + for ; i+vl-1 <= hi; i += vl { + left := remB.Sub(idx).Min(idx.Add(offA)) + acc = acc.Max(simd.LoadUint16s(row[i:]).Add(left)) + idx = idx.Add(step) + } + var lanes [64]uint16 + acc.Store(lanes[:]) + for _, v := range lanes[:vl] { + bound = max(bound, int(v)) + } + } + for ; i <= hi; i++ { + bound = max(bound, int(row[i])+min(lB-i, lA-t+i)) + } + + // the paths running along the first row or the first column have a score of 0 + if t <= lA && t <= kmax { + bound = max(bound, min(lB, lA-t)) + } + if t <= lB && -t >= kmin { + bound = max(bound, min(lB-t, lA)) + } + return bound +} diff --git a/pkg/obitools/obiclust/block.go b/pkg/obitools/obiclust/block.go new file mode 100644 index 0000000..487b5a5 --- /dev/null +++ b/pkg/obitools/obiclust/block.go @@ -0,0 +1,46 @@ +package obiclust + +// A block stores the 4-mer tables of a few centers, transposed: the counter +// of the word w for the lane j is data[w*width+j]. A vector instruction then +// handles the same word for all the centers of the block, and only the words +// that a sequence contains (a hundred for a short amplicon, out of 256) need +// to be visited to compare it with all the centers of the block. +type tableBlock struct { + data []uint8 +} + +func newTableBlock(width int) tableBlock { + return tableBlock{data: make([]uint8, 256*width)} +} + +// set stores the table t as the lane `lane` of the block. +func (b *tableBlock) set(width, lane int, t *table4) { + for w := range t.count { + b.data[w*width+lane] = t.count[w] + } +} + +// common computes the sums of the minima of the counters of the sequence +// given by its words with every lane of the block (see blockCommon). +func (b *tableBlock) common(width int, words []uint16, out []uint8) { + blockCommon(b.data, width, words, out) +} + +// sparseWords lists the words of a table as w<<8|count, so that the visit of +// a block only goes through the words present in the sequence. nil is +// returned when the counters add up to more than 255, which would overflow the +// 8-bit sums of the blocks. +func sparseWords(t *table4) []uint16 { + words := make([]uint16, 0, 128) + mass := 0 + for w, c := range t.count { + if c > 0 { + words = append(words, uint16(w)<<8|uint16(c)) + mass += int(c) + } + } + if mass > 255 { + return nil + } + return words +} diff --git a/pkg/obitools/obiclust/block_nosimd.go b/pkg/obitools/obiclust/block_nosimd.go new file mode 100644 index 0000000..7353d29 --- /dev/null +++ b/pkg/obitools/obiclust/block_nosimd.go @@ -0,0 +1,10 @@ +//go:build !goexperiment.simd + +package obiclust + +// simdBlockWidth is 0 without SIMD: the blocks are not used. +func simdBlockWidth() int { return 0 } + +func blockCommon(data []uint8, width int, words []uint16, out []uint8) { + panic("obiclust: blocks of tables require GOEXPERIMENT=simd") +} diff --git a/pkg/obitools/obiclust/block_simd.go b/pkg/obitools/obiclust/block_simd.go new file mode 100644 index 0000000..b462de0 --- /dev/null +++ b/pkg/obitools/obiclust/block_simd.go @@ -0,0 +1,25 @@ +//go:build goexperiment.simd + +package obiclust + +import "simd" + +// simdBlockWidth is the number of centers in a block: one per lane of a +// vector of bytes. +func simdBlockWidth() int { + var probe simd.Uint8s + return min(probe.Len(), 64) +} + +// blockCommon computes, for the sequence whose words are given, the sum of +// the minima of the counters with every lane of the block. It is the value of +// commonWords without the overflow term, as the sums fit in 8 bits (see +// sparseWords). out must hold at least `width` bytes. +func blockCommon(data []uint8, width int, words []uint16, out []uint8) { + acc := simd.BroadcastUint8s(0) + for _, e := range words { + w := simd.LoadUint8s(data[int(e>>8)*width:]) + acc = acc.Add(w.Min(simd.BroadcastUint8s(uint8(e)))) + } + acc.Store(out) +} diff --git a/pkg/obitools/obiclust/centers.go b/pkg/obitools/obiclust/centers.go new file mode 100644 index 0000000..c88cf83 --- /dev/null +++ b/pkg/obitools/obiclust/centers.go @@ -0,0 +1,136 @@ +package obiclust + +import ( + "math" + "sort" + + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obialign" +) + +// center is a copy of the entry of a center, with its own table: scanning +// the centers of a list then reads contiguous memory. +type center struct { + entry + idx int // index of the entry in the dataset + rank int // rank of the center in the order of creation +} + +const tabChunk = 1024 + +// A bucket holds the centers of one length, in the order of creation, and +// their tables by blocks (see tableBlock). +type bucket struct { + cents []center + blocks []tableBlock +} + +// centerSet stores the centers by length. A sequence can only be attached to a +// center whose length is compatible with its own (see lengthCompatible), so +// it only visits the buckets of these lengths, instead of all the centers. +type centerSet struct { + width int // centers per block of transposed tables, 0 if unused + byLen map[int]*bucket + lens []int // lengths that have a bucket, increasing + tabs [][]table4 // chunks holding the tables of the centers + n int +} + +func newCenterSet(width int) *centerSet { + return ¢erSet{width: width, byLen: make(map[int]*bucket)} +} + +// add stores a copy of the entry e, which is the entry idx of the dataset, as +// the next center, and returns it. +func (s *centerSet) add(e entry, idx int) center { + if s.n%tabChunk == 0 { + s.tabs = append(s.tabs, make([]table4, tabChunk)) + } + tab := &s.tabs[len(s.tabs)-1][s.n%tabChunk] + if e.table != nil { + *tab = *e.table + } + e.table = tab + + b := s.byLen[e.length] + if b == nil { + b = new(bucket) + s.byLen[e.length] = b + s.lens = append(s.lens, e.length) + sort.Ints(s.lens) + } + if w := s.width; w > 0 && e.table != nil { + if len(b.cents)%w == 0 { + b.blocks = append(b.blocks, newTableBlock(w)) + } + b.blocks[len(b.blocks)-1].set(w, len(b.cents)%w, tab) + } + + ctr := center{entry: e, idx: idx, rank: s.n} + b.cents = append(b.cents, ctr) + s.n++ + return ctr +} + +// scan compares entry i with all the centers of the set, and returns the +// first one it matches (in fast mode) or the best one (in exact mode, the +// first of them in case of tie), exactly as compare would on the list of the +// centers in the order of creation. +// +// Only the buckets of compatible lengths are visited: the other centers would +// give the worst score. In each bucket the first match is the one of lowest +// rank, so the first match of the whole set is the one of lowest rank among +// the buckets. In exact mode, the best of each bucket is compared, ties going +// to the lowest rank. +func (c *clustering) scan(i int, buf *obialign.BandedLCSBuffer) attachment { + q := &c.entries[i] + set := c.set + width := set.width + useBlocks := width > 0 && q.sparse != nil + + best := attachment{-1, c.sc.worst} + bestRank := math.MaxInt + var common [64]uint8 + + for _, length := range set.lens { + if !c.sc.lengthCompatible(q.length, length) { + continue + } + b := set.byLen[length] + local := attachment{-1, c.sc.worst} + localRank := 0 + + for k := range b.cents { + ctr := &b.cents[k] + if c.fast && ctr.rank > bestRank { + break // a center of lower rank has been found in another bucket + } + cw := -1 + if useBlocks { + if k%width == 0 { + b.blocks[k/width].common(width, q.sparse, common[:]) + } + cw = int(common[k%width]) + } + s := c.sc.pairScore(q, &ctr.entry, cw, buf) + if c.sc.selects(s, local.score) { + local, localRank = attachment{ctr.idx, s}, ctr.rank + if c.fast { + break + } + } + } + + if local.center < 0 { + continue + } + if c.fast { + if localRank < bestRank { + best, bestRank = local, localRank + } + } else if best.center < 0 || c.sc.beats(local.score, best.score) || + (local.score == best.score && localRank < bestRank) { + best, bestRank = local, localRank + } + } + return best +} diff --git a/pkg/obitools/obiclust/cluster.go b/pkg/obitools/obiclust/cluster.go new file mode 100644 index 0000000..2e8835e --- /dev/null +++ b/pkg/obitools/obiclust/cluster.go @@ -0,0 +1,202 @@ +package obiclust + +import ( + "sort" + "sync" + "sync/atomic" + + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obialign" +) + +// clustering is the state of the star clustering of the distinct sequences, +// taken in their sorted order. +// +// The reference algorithm is sequential: each sequence is compared with the +// centers created so far, in the order of their creation, and is attached to +// the first one it matches (or to the best one in exact mode), or becomes a +// new center. Its result is reproduced exactly while the comparisons are run +// in parallel, by processing the sequences by blocks: +// +// - phase A (parallel): every sequence of the block is compared with the +// centers existing when the block starts. This is independent from one +// sequence to the other, a sequence of the block cannot change the centers; +// - phase B (sequential): the sequences matching no center are then taken +// in their order. Each one is compared with the centers created earlier in +// the block, and becomes a new center if it still matches nothing. The +// new centers come after the old ones in the order of the centers, so +// the result is the one of the sequential algorithm; +// - phase C (parallel, exact mode only): the sequences attached in phase A +// are compared with the new centers that precede them, the best score of +// the whole list of centers being searched for. +// +// The result does not depend on the size of the blocks, which only trades the +// sequential work of phase B against the synchronizations. +type clustering struct { + entries []entry + sc *scoring + fast bool + workers int + // bounds of the size of the blocks, 0 for the defaults (tests only) + blockMin, blockMax int + + centers []int // indices of the centers, in the order of creation + set *centerSet + fresh []center // the centers created in the current block + + center []int // for each entry, the index of its center, -1 if it has none + score []float64 // for each entry, the score with its center + bufs []obialign.BandedLCSBuffer +} + +// result of the comparison of one entry with the list of centers +type attachment struct { + center int // index of the center, -1 if none + score float64 +} + +func newClustering(entries []entry, sc *scoring, fast bool, workers int) *clustering { + c := &clustering{ + entries: entries, + sc: sc, + fast: fast, + workers: max(workers, 1), + center: make([]int, len(entries)), + score: make([]float64, len(entries)), + bufs: make([]obialign.BandedLCSBuffer, max(workers, 1)), + } + for i := range c.center { + c.center[i] = -1 + } + c.set = newCenterSet(simdBlockWidth()) + return c +} + +func (c *clustering) addCenter(i int) { + c.fresh = append(c.fresh, c.set.add(c.entries[i], i)) + c.centers = append(c.centers, i) + c.center[i] = i + c.score[i] = c.sc.centerScore(c.entries[i].length) +} + +// compare looks for the best center of entry i among the given ones. The +// attachment `from` is the best one already known. The search stops at the +// first match in fast mode. +func (c *clustering) compare(i int, cents []center, from attachment, buf *obialign.BandedLCSBuffer) attachment { + best := from + for k := range cents { + s := c.sc.pairScore(&c.entries[i], ¢s[k].entry, -1, buf) + if c.sc.selects(s, best.score) { + best = attachment{cents[k].idx, s} + if c.fast { + break + } + } + } + return best +} + +// parallelFor runs f(i, buf) for i in [from, to) on the workers. +func (c *clustering) parallelFor(from, to int, f func(i int, buf *obialign.BandedLCSBuffer)) { + n := to - from + if c.workers == 1 || n == 1 { + for i := from; i < to; i++ { + f(i, &c.bufs[0]) + } + return + } + + var next atomic.Int64 + next.Store(int64(from)) + var wg sync.WaitGroup + for w := 0; w < min(c.workers, n); w++ { + wg.Add(1) + go func(buf *obialign.BandedLCSBuffer) { + defer wg.Done() + for { + i := int(next.Add(1)) - 1 + if i >= to { + return + } + f(i, buf) + } + }(&c.bufs[w]) + } + wg.Wait() +} + +// run clusters all the entries. progress is called with the number of +// entries processed. +func (c *clustering) run(progress func(done int)) { + n := len(c.entries) + if n == 0 { + return + } + + minBlock, maxBlock := 2*c.workers, 256*c.workers + if c.blockMax > 0 { + minBlock, maxBlock = c.blockMin, c.blockMax + } + block := minBlock + att := make([]attachment, maxBlock) + inA := make([]bool, maxBlock) // attached to a center existing before the block + var newCenters []int // indices of the centers created in the block + + for pos := 0; pos < n; { + end := min(n, pos+block) + c.fresh = c.fresh[:0] + att, inA := att[:end-pos], inA[:end-pos] + + // phase A + c.parallelFor(pos, end, func(i int, buf *obialign.BandedLCSBuffer) { + att[i-pos] = c.scan(i, buf) + inA[i-pos] = att[i-pos].center >= 0 + }) + + // phase B + unmatched := 0 + newCenters = newCenters[:0] + for i := pos; i < end; i++ { + a := &att[i-pos] + if inA[i-pos] { + continue + } + unmatched++ + *a = c.compare(i, c.fresh, *a, &c.bufs[0]) + if a.center < 0 && !c.entries[i].ambiguous { + newCenters = append(newCenters, i) + c.addCenter(i) + } + } + + // phase C + if !c.fast && len(newCenters) > 0 { + c.parallelFor(pos, end, func(i int, buf *obialign.BandedLCSBuffer) { + if inA[i-pos] { + before := c.fresh[:sort.SearchInts(newCenters, i)] + att[i-pos] = c.compare(i, before, att[i-pos], buf) + } + }) + } + + for i := pos; i < end; i++ { + if a := att[i-pos]; a.center >= 0 { + c.center[i] = a.center + c.score[i] = c.sc.memberScore(a.score) + } + } + + // Size of the next block. Phase B is sequential: for a block of b + // sequences of which a fraction u matches no old center and a fraction + // n becomes new centers, it makes about b*u*b*n/2 comparisons, against + // b*K/workers in the time of phase A (K old centers). The block is + // sized so that phase B stays around a tenth of phase A. + if fu, fn := float64(unmatched)/float64(end-pos), float64(len(newCenters))/float64(end-pos); fu*fn > 0 { + target := 0.2 * float64(len(c.centers)) / (float64(c.workers) * fu * fn) + block = int(min(float64(maxBlock), max(float64(minBlock), target))) + } else { + block = maxBlock + } + pos = end + progress(pos) + } +} diff --git a/pkg/obitools/obiclust/kmer4.go b/pkg/obitools/obiclust/kmer4.go new file mode 100644 index 0000000..71db5bd --- /dev/null +++ b/pkg/obitools/obiclust/kmer4.go @@ -0,0 +1,47 @@ +package obiclust + +// table4 is the 4-mer occurrence table of a sequence, as sumaclust builds it: +// one saturating counter per word, and the number of occurrences that did +// not fit in the counters. +type table4 struct { + count [256]uint8 + overflow int +} + +// buildTable4 counts the 4-mers of a sequence made of the letters a, c, g +// and t. The 2-bit code of a base is (letter>>1)&3, i.e. a=0, c=1, t=2, g=3, +// which is the encoding of sumaclust. +func buildTable4(seq []byte) *table4 { + t := new(table4) + t.fill(seq) + return t +} + +// fill counts the 4-mers of seq in a zeroed table. +func (t *table4) fill(seq []byte) { + if len(seq) < 4 { + return + } + w := uint(0) + for i, c := range seq { + w = (w<<2 | uint(c>>1)&3) & 0xFF + if i < 3 { + continue + } + if t.count[w] < 255 { + t.count[w]++ + } else { + t.overflow++ + } + } +} + +// minCommonWords is the minimum number of 4-mers that two sequences must +// share when the longest has length reflen and they have a common +// subsequence of length lcs (integer arithmetic of sumaclust, thresholdLCS4). +func minCommonWords(reflen, lcs int) int { + nbfrag := (reflen-lcs)*2 + 1 + smin := lcs / nbfrag + r := lcs - smin*nbfrag + return max(smin-2, 0)*r + max(smin-3, 0)*(nbfrag-r) +} diff --git a/pkg/obitools/obiclust/kmer4_nosimd.go b/pkg/obitools/obiclust/kmer4_nosimd.go new file mode 100644 index 0000000..01e32a4 --- /dev/null +++ b/pkg/obitools/obiclust/kmer4_nosimd.go @@ -0,0 +1,14 @@ +//go:build !goexperiment.simd + +package obiclust + +// commonWords is the upper bound sumaclust uses for the number of 4-mers +// shared by two sequences: the sum of the minima of the counters, plus the +// minimum of the two overflows. +func commonWords(t1, t2 *table4) int { + total := 0 + for i := range t1.count { + total += int(min(t1.count[i], t2.count[i])) + } + return total + min(t1.overflow, t2.overflow) +} diff --git a/pkg/obitools/obiclust/kmer4_simd.go b/pkg/obitools/obiclust/kmer4_simd.go new file mode 100644 index 0000000..96da178 --- /dev/null +++ b/pkg/obitools/obiclust/kmer4_simd.go @@ -0,0 +1,35 @@ +//go:build goexperiment.simd + +package obiclust + +import "simd" + +// commonWords is the upper bound sumaclust uses for the number of 4-mers +// shared by two sequences: the sum of the minima of the counters, plus the +// minimum of the two overflows. +// +// The minima of the 256 counters are computed by vectors of bytes, and summed +// in 16-bit lanes (at most 256*255 spread over the lanes, the lanes being at +// least 8 in number, so that none of them overflows). +func commonWords(t1, t2 *table4) int { + var probe simd.Uint8s + vl := probe.Len() + + acc := simd.BroadcastUint16s(0) + low := simd.BroadcastUint16s(0x00FF) + for i := 0; i < len(t1.count); i += vl { + m := simd.LoadUint8s(t1.count[i:]).Min(simd.LoadUint8s(t2.count[i:])).ReshapeToUint16s() + acc = acc.Add(m.And(low)).Add(m.ShiftAllRight(8)) + } + + // sum the pairs of 16-bit lanes into 32-bit lanes, then add those up + wide := acc.ReshapeToUint32s() + sums := wide.And(simd.BroadcastUint32s(0xFFFF)).Add(wide.ShiftAllRight(16)) + var lanes [32]uint32 + sums.Store(lanes[:]) + total := 0 + for _, v := range lanes[:sums.Len()] { + total += int(v) + } + return total + min(t1.overflow, t2.overflow) +} diff --git a/pkg/obitools/obiclust/kmer4_test.go b/pkg/obitools/obiclust/kmer4_test.go new file mode 100644 index 0000000..66e8f34 --- /dev/null +++ b/pkg/obitools/obiclust/kmer4_test.go @@ -0,0 +1,71 @@ +package obiclust + +import ( + "math/rand" + "testing" +) + +// commonWordsRef is the definition of commonWords, spelled out. +func commonWordsRef(t1, t2 *table4) int { + total := 0 + for i := range t1.count { + total += int(min(t1.count[i], t2.count[i])) + } + return total + min(t1.overflow, t2.overflow) +} + +func TestCommonWords(t *testing.T) { + rng := rand.New(rand.NewSource(3)) + seq := func(n int, alpha string) []byte { + s := make([]byte, n) + for i := range s { + s[i] = alpha[rng.Intn(len(alpha))] + } + return s + } + for it := 0; it < 3000; it++ { + alpha := []string{"acgt", "ac", "a"}[it%3] + t1 := buildTable4(seq(rng.Intn(1200), alpha)) + t2 := buildTable4(seq(rng.Intn(1200), alpha)) + if got, want := commonWords(t1, t2), commonWordsRef(t1, t2); got != want { + t.Fatalf("commonWords = %d, want %d", got, want) + } + } +} + +// TestBuildTable4 checks the table against the 4-mers counted the long way, +// including the saturation of the counters. +func TestBuildTable4(t *testing.T) { + code := map[byte]int{'a': 0, 'c': 1, 't': 2, 'g': 3} + rng := rand.New(rand.NewSource(4)) + for it := 0; it < 500; it++ { + alpha := []string{"acgt", "a", "ac"}[it%3] + n := rng.Intn(900) + s := make([]byte, n) + for i := range s { + s[i] = alpha[rng.Intn(len(alpha))] + } + var counts [256]int + for i := 0; i+4 <= n; i++ { + w := 0 + for _, c := range s[i : i+4] { + w = w<<2 | code[c] + } + counts[w]++ + } + tab := buildTable4(s) + over := 0 + for w, c := range counts { + if c > 255 { + over += c - 255 + c = 255 + } + if int(tab.count[w]) != c { + t.Fatalf("word %d: got %d, want %d", w, tab.count[w], c) + } + } + if tab.overflow != over { + t.Fatalf("overflow = %d, want %d", tab.overflow, over) + } + } +} diff --git a/pkg/obitools/obiclust/obiclust.go b/pkg/obitools/obiclust/obiclust.go index 5ffa951..1342db5 100644 --- a/pkg/obitools/obiclust/obiclust.go +++ b/pkg/obitools/obiclust/obiclust.go @@ -1 +1,225 @@ package obiclust + +import ( + "os" + "sync" + "sync/atomic" + + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obialign" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obidefault" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiiter" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiseq" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obitools/obiconvert" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obitools/obicsv" + "github.com/schollz/progressbar/v3" + log "github.com/sirupsen/logrus" +) + +// buildEntries describes the distinct sequences of the groups, the 4-mer +// tables being computed in parallel. +func buildEntries(recs []record, groups []group, workers int) []entry { + entries := make([]entry, len(groups)) + tables := make([]table4, len(groups)) // contiguous, in the order the centers are scanned + + var next atomic.Int64 + var wg sync.WaitGroup + for w := 0; w < max(workers, 1); w++ { + wg.Add(1) + go func() { + defer wg.Done() + for { + g := int(next.Add(1)) - 1 + if g >= len(groups) { + return + } + head := &recs[groups[g].start] + var sparse []uint16 + if !head.ambiguous { + tables[g].fill(head.clean) + sparse = sparseWords(&tables[g]) + } + entries[g] = entry{ + seq: head.clean, + length: len(head.clean), + count: groups[g].count, + table: &tables[g], + sparse: sparse, + ambiguous: head.ambiguous, + } + } + }() + } + wg.Wait() + return entries +} + +// Params are the parameters of the clustering. +type Params struct { + Threshold float64 + Ratio float64 + Exact bool // attach a sequence to the best center, not to the first matching one + Distance bool // the score is a distance, not a similarity + Normalized bool // the score is normalized by the reference length + Reference reference + SortKey string // "count", "None", or an attribute + Ascending bool + AcceptAmbiguous bool // keep the sequences with IUPAC letters other than acgt (they cannot be centers) + Workers int +} + +// ParamsFromCLI collects the parameters from the command line options. +func ParamsFromCLI() Params { + return Params{ + Threshold: CLIThreshold(), + Ratio: CLIRatio(), + Exact: CLIExact(), + Distance: CLIDistance(), + Normalized: CLINormalized(), + Reference: CLIReference(), + SortKey: CLISortKey(), + Ascending: CLIAscending(), + AcceptAmbiguous: CLIAcceptAmbiguous(), + Workers: obidefault.ParallelWorkers(), + } +} + +// newScoring builds the scoring from the parameters, checking them as sumaclust does. +func newScoring(p Params, entries []entry) *scoring { + threshold := p.Threshold + + if threshold <= 0 || (p.Normalized && threshold > 1.0) { + log.Fatal("Please specify a threshold > 0, and < 1 when scores are normalized.") + } + + sc := &scoring{ + normalize: p.Normalized, + lcsmode: !p.Distance, + ref: p.Reference, + ratio: p.Ratio, + } + if !sc.lcsmode && sc.normalize { + threshold = 1.0 - threshold + } + sc.threshold = threshold + + lmax := 0 + for i := range entries { + lmax = max(lmax, entries[i].length) + } + if sc.similarityLike() { + sc.worst = 0.0 + } else { + sc.worst = float64(lmax) + } + sc.prepareLengths(lmax) + return sc +} + +func progressBar(n int) *progressbar.ProgressBar { + if !obiconvert.CLIProgressBar() { + return nil + } + return progressbar.NewOptions(n, + progressbar.OptionSetWriter(os.Stderr), + progressbar.OptionSetWidth(15), + progressbar.OptionShowIts(), + progressbar.OptionSetPredictTime(true), + progressbar.OptionSetDescription("[Clustering sequences]"), + ) +} + +// Result is a clustering of a dataset. +type Result struct { + recs []record + groups []group + clustered *clustering + Discarded int // number of sequences that were not usable +} + +// Clusters returns the number of clusters. +func (r *Result) Clusters() int { return len(r.clustered.centers) } + +// Cluster clusters the sequences with the star clustering algorithm of +// sumaclust (the same as UCLUST and CD-HIT). progress, if not nil, is called +// with the number of distinct sequences processed. +func Cluster(db obiseq.BioSequenceSlice, p Params, progress func(done, total int)) *Result { + recs, groups, discarded := prepare(db, p.SortKey, p.Ascending, p.AcceptAmbiguous) + + if discarded > 0 { + log.Warnf("Discarded %d sequences that did not contain only IUPAC nucleotides (or only 'acgt' without --accept-ambiguous)", discarded) + } + if len(groups) == 0 { + log.Fatal("No valid sequences.") + } + log.Infof("%d distinct sequences", len(groups)) + + entries := buildEntries(recs, groups, p.Workers) + for i := range entries { + if entries[i].length > obialign.MaxBandedLCSLength { + log.Fatalf("The sequence %s is too long (%d bp, maximum %d bp)", + recs[groups[i].start].seq.Id(), entries[i].length, obialign.MaxBandedLCSLength) + } + } + + sc := newScoring(p, entries) + if sc.similarityLike() { + log.Infof("Clustering sequences when similarity >= %f", sc.threshold) + } else { + log.Infof("Clustering sequences when distance <= %f", sc.threshold) + } + + cl := newClustering(entries, sc, !p.Exact, p.Workers) + cl.run(func(done int) { + if progress != nil { + progress(done, len(entries)) + } + }) + log.Infof("%d clusters created", len(cl.centers)) + + return &Result{recs: recs, groups: groups, clustered: cl, Discarded: discarded} +} + +// CLIOBIClust clusters the sequences and returns them annotated. +func CLIOBIClust(iterator obiiter.IBioSequence) obiiter.IBioSequence { + source, db := iterator.Load() + log.Infof("Sequence dataset of %d sequences loaded", len(db)) + + var bar *progressbar.ProgressBar + last := 0 + res := Cluster(db, ParamsFromCLI(), func(done, total int) { + if bar == nil { + bar = progressBar(total) + } + if bar != nil { + bar.Add(done - last) + } + last = done + }) + + if CLIOTUMap() != "" { + table, err := obicsv.WriteCSVToFile( + res.OTUMapIterator(source, obidefault.BatchSize()), + CLIOTUMap(), + obicsv.OptionsCompressed(obidefault.CompressOutput())) + if err != nil { + log.Fatalf("Cannot write the OTU map %s: %v", CLIOTUMap(), err) + } + table.Consume() + } + + if CLINoSequenceOutput() { + return emptyIterator() + } + return obiiter.IBatchOver(source, res.Annotate(), obidefault.BatchSize()) +} + +// emptyIterator returns an iterator that is already exhausted. +func emptyIterator() obiiter.IBioSequence { + iterator := obiiter.MakeIBioSequence() + iterator.Add(1) + go func() { + iterator.WaitAndClose() + }() + iterator.Done() + return iterator +} diff --git a/pkg/obitools/obiclust/obiclust_test.go b/pkg/obitools/obiclust/obiclust_test.go new file mode 100644 index 0000000..fff004e --- /dev/null +++ b/pkg/obitools/obiclust/obiclust_test.go @@ -0,0 +1,434 @@ +package obiclust + +import ( + "bufio" + "fmt" + "math/rand" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obialign" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiseq" +) + +// readTestFasta reads a fasta file whose headers are "id count=N; key=V;". +func readTestFasta(t testing.TB, filename string) obiseq.BioSequenceSlice { + t.Helper() + f, err := os.Open(filename) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + var db obiseq.BioSequenceSlice + var header string + var seq strings.Builder + flush := func() { + if header == "" { + return + } + fields := strings.Fields(header) + s := obiseq.NewBioSequence(fields[0], []byte(seq.String()), "") + for _, f := range fields[1:] { + if k, v, ok := strings.Cut(strings.TrimSuffix(f, ";"), "="); ok { + if n, err := strconv.Atoi(v); err == nil { + s.SetAttribute(k, n) + } + } + } + db = append(db, s) + seq.Reset() + } + sc := bufio.NewScanner(f) + sc.Buffer(make([]byte, 1<<20), 1<<20) + for sc.Scan() { + if line := sc.Text(); strings.HasPrefix(line, ">") { + flush() + header = line[1:] + } else { + seq.WriteString(strings.TrimSpace(line)) + } + } + flush() + return db +} + +// paramsFromSumaclust translates the options of sumaclust. +func paramsFromSumaclust(t testing.TB, args []string) Params { + t.Helper() + p := Params{Threshold: 0.97, Ratio: 1.0, Normalized: true, SortKey: "count", Workers: 4} + for i := 0; i < len(args); i++ { + var err error + switch args[i] { + case "-t": + i++ + p.Threshold, err = strconv.ParseFloat(args[i], 64) + case "-R": + i++ + p.Ratio, err = strconv.ParseFloat(args[i], 64) + case "-s": + i++ + p.SortKey = args[i] + case "-e": + p.Exact = true + case "-l": + p.Reference = refShortest + case "-L": + p.Reference = refLongest + case "-a": + p.Reference = refAlignment + case "-d": + p.Distance = true + case "-r": + p.Normalized = false + case "-o": + p.Ascending = true + case "-g": + p.AcceptAmbiguous = true + default: + t.Fatalf("unknown option %s", args[i]) + } + if err != nil { + t.Fatal(err) + } + } + return p +} + +// TestSumaclustResults compares the clusterings with the outputs of +// sumaclust 1.0.36 (testdata/*.expected: id, cluster, score, center, weight +// and sequence of every record, in the order sumaclust writes them). +func TestSumaclustResults(t *testing.T) { + cases, err := os.ReadFile(filepath.Join("testdata", "cases.txt")) + if err != nil { + t.Fatal(err) + } + for _, line := range strings.Split(strings.TrimSpace(string(cases)), "\n") { + parts := strings.Split(line, "\t") + data, name, opts := parts[0], parts[1], strings.Fields(parts[2]) + t.Run(data+"/"+name, func(t *testing.T) { + db := readTestFasta(t, filepath.Join("testdata", data+".fasta")) + res := Cluster(db, paramsFromSumaclust(t, opts), nil) + + expected, err := os.ReadFile(filepath.Join("testdata", data+"_"+name+".expected")) + if err != nil { + t.Fatal(err) + } + want := strings.Split(strings.TrimSpace(string(expected)), "\n") + got := res.Annotate() + if len(got) != len(want) { + t.Fatalf("%d records, want %d", len(got), len(want)) + } + for i, s := range got { + score, _ := s.GetFloatAttribute("cluster_score") + center, _ := s.GetBoolAttribute("cluster_center") + c := map[bool]string{true: "T", false: "F"}[center] + cluster, _ := s.GetStringAttribute("cluster") + weight, _ := s.GetIntAttribute("cluster_weight") + line := fmt.Sprintf("%s\t%s\t%f\t%s\t%d\t%s", s.Id(), cluster, score, c, weight, s.String()) + if line != want[i] { + t.Fatalf("record %d:\n got %s\nwant %s", i, line, want[i]) + } + } + }) + } +} + +// referenceCluster transcribes the sequential algorithm of sumaclust. +func referenceCluster(entries []entry, sc *scoring, fast bool) (center []int, score []float64) { + n := len(entries) + center, score = make([]int, n), make([]float64, n) + var centers []int + var buf obialign.BandedLCSBuffer + for i := range entries { + scoremax, best := sc.worst, -1 + for _, c := range centers { + s := sc.pairScore(&entries[i], &entries[c], -1, &buf) + if sc.selects(s, scoremax) { + scoremax, best = s, c + if fast { + break + } + } + } + if best >= 0 { + center[i], score[i] = best, sc.memberScore(scoremax) + } else if entries[i].ambiguous { + center[i] = -1 + } else { + center[i], score[i] = i, sc.centerScore(entries[i].length) + centers = append(centers, i) + } + } + return center, score +} + +func randomEntries(rng *rand.Rand, n int) []entry { + var roots [][]byte + entries := make([]entry, 0, n) + for len(entries) < n { + var s []byte + if len(roots) == 0 || rng.Intn(5) == 0 { + s = make([]byte, 30+rng.Intn(50)) + for i := range s { + s[i] = "acgt"[rng.Intn(4)] + } + roots = append(roots, s) + } else { + root := roots[rng.Intn(len(roots))] + for _, c := range root { + switch r := rng.Intn(40); { + case r == 0: + s = append(s, "acgt"[rng.Intn(4)]) + case r == 1: + case r == 2: + s = append(s, c, "acgt"[rng.Intn(4)]) + default: + s = append(s, c) + } + } + } + tab := buildTable4(s) + entries = append(entries, entry{seq: s, length: len(s), count: 1 + rng.Intn(20), table: tab, sparse: sparseWords(tab)}) + } + return entries +} + +// TestParallelClusteringIsSequential checks that the clustering by blocks +// gives, whatever the number of workers and the size of the blocks, the +// result of the sequential algorithm. +func TestParallelClusteringIsSequential(t *testing.T) { + rng := rand.New(rand.NewSource(21)) + scorings := []scoring{ + {threshold: 0.9, normalize: true, lcsmode: true, ref: refAlignment, ratio: 1, worst: 0}, + {threshold: 0.85, normalize: true, lcsmode: true, ref: refShortest, ratio: 2, worst: 0}, + {threshold: 0.9, normalize: true, lcsmode: false, ref: refLongest, ratio: 1, worst: 0}, + {threshold: 35, normalize: false, lcsmode: true, ref: refAlignment, ratio: 1, worst: 0}, + {threshold: 5, normalize: false, lcsmode: false, ref: refAlignment, ratio: 1, worst: 80}, + } + for round := 0; round < 4; round++ { + entries := randomEntries(rng, 300) + for si := range scorings { + sc := scorings[si] + for _, fast := range []bool{true, false} { + wantCenter, wantScore := referenceCluster(entries, &sc, fast) + for _, workers := range []int{1, 3, 8} { + for _, blocks := range [][2]int{{0, 0}, {1, 1}, {2, 5}, {7, 7}, {50, 300}} { + cl := newClustering(entries, &sc, fast, workers) + cl.blockMin, cl.blockMax = blocks[0], blocks[1] + cl.run(func(int) {}) + for i := range entries { + if cl.center[i] != wantCenter[i] || cl.score[i] != wantScore[i] { + t.Fatalf("scoring %d fast=%v workers=%d blocks=%v: entry %d is (%d, %v), want (%d, %v)", + si, fast, workers, blocks, i, cl.center[i], cl.score[i], wantCenter[i], wantScore[i]) + } + } + } + } + } + } + } +} + +func TestClassify(t *testing.T) { + cases := []struct { + in string + accept bool + kept bool + ambiguous bool + }{ + {"acgt", false, true, false}, {"acgt", true, true, false}, + {"acgtn", false, false, false}, {"acgtn", true, true, true}, + {"acgtx", true, false, false}, {"nacgt", false, false, false}, + {"acgt-", true, false, false}, {"acgu", true, true, true}, + {"ACGT", true, false, false}, {"", true, false, false}, + {"rykmswbdhvn", true, true, true}, {"rykmswbdhvn", false, false, false}, + } + for _, c := range cases { + got, amb := classify([]byte(c.in), c.accept) + if (got != nil) != c.kept || amb != c.ambiguous { + t.Errorf("classify(%q, %v) = (%q, %v), want kept=%v ambiguous=%v", c.in, c.accept, got, amb, c.kept, c.ambiguous) + } + } +} + +func makeSeq(id, seq string, count int, key float64) *obiseq.BioSequence { + s := obiseq.NewBioSequence(id, []byte(seq), "") + s.SetAttribute("count", count) + s.SetAttribute("key", key) + return s +} + +func ids(groups []group, recs []record) string { + var out []string + for _, g := range groups { + out = append(out, recs[g.start].seq.Id()) + } + return strings.Join(out, " ") +} + +// TestPrepareOrder checks the order of the sequences, which does not depend +// on the stability of a sort: decreasing count, then position; the head of a +// group of identical sequences is the one with the extreme key, then the +// first one. +func TestPrepareOrder(t *testing.T) { + db := obiseq.BioSequenceSlice{ + makeSeq("a", "aaaacccc", 5, 1), + makeSeq("b", "gggg", 9, 2), + makeSeq("c", "aaaacccc", 5, 3), // same sequence as a, same count: a is the head + makeSeq("d", "tttt", 10, 4), + makeSeq("e", "cccc", 10, 5), // same cumulated count as d: after d, which comes first + makeSeq("f", "aaaacccc", 7, 6), + makeSeq("g", "nnnn", 3, 7), // discarded + makeSeq("h", "ccgg", 2, 8), + } + + check := func(key string, ascending bool, want string) { + recs, groups, discarded := prepare(db, key, ascending, false) + if discarded != 1 { + t.Errorf("discarded = %d, want 1", discarded) + } + if got := ids(groups, recs); got != want { + t.Errorf("key=%s ascending=%v: got %s, want %s", key, ascending, got, want) + } + } + // a, c and f are one group of count 17, headed by f (count 7) + check("count", false, "f d e b h") + check("count", true, "h b d e a") // the head of a group is then the record with the smallest count + // on the key attribute, the groups are ranked on the key of their head, the record with the largest key + check("key", false, "h f e d b") + check("key", true, "a b d e h") + // no sorting: first occurrence of each sequence + check("None", false, "a b d e h") + + // the identical sequences follow their head, with the largest count first + recs, groups, _ := prepare(db, "count", false, false) + var members []string + for r := groups[0].start; r < groups[0].end; r++ { + members = append(members, recs[r].seq.Id()) + } + if got := strings.Join(members, " "); got != "f a c" { + t.Errorf("group of f: %s, want f a c", got) + } +} + +// TestAmbiguousSequences checks that a sequence with IUPAC letters is attached +// to a cluster when it matches a center, and is left without cluster otherwise. +func TestAmbiguousSequences(t *testing.T) { + root := "acgtacgtaccgtagctagcatcgatcgatcgtagctagctagcatcg" + db := obiseq.BioSequenceSlice{ + makeSeq("center", root, 10, 0), + makeSeq("amb", root[:20]+"n"+root[21:], 5, 0), // matches the center + makeSeq("far", "ttttgggggccccaaaaattttgggggccccaaaaattttggggg", 4, 0), + makeSeq("ambfar", "nnnnttttggggcccccaaaa", 3, 0), // matches nothing, cannot be a center + } + p := Params{Threshold: 0.9, Ratio: 1, Normalized: true, SortKey: "count", AcceptAmbiguous: true, Workers: 2} + res := Cluster(db, p, nil) + out := res.Annotate() + got := map[string]string{} + for _, s := range out { + c, ok := s.GetStringAttribute("cluster") + if !ok { + c = "-" + } + got[s.Id()] = c + } + want := map[string]string{"center": "center", "amb": "center", "far": "far", "ambfar": "-"} + for id, w := range want { + if got[id] != w { + t.Errorf("%s: cluster %q, want %q", id, got[id], w) + } + } + if res.Clusters() != 2 { + t.Errorf("%d clusters, want 2", res.Clusters()) + } + for _, s := range out { + if s.Id() == "ambfar" { + if center, _ := s.GetBoolAttribute("cluster_center"); center || s.HasAttribute("cluster_weight") { + t.Error("ambfar must have cluster_center=false and no weight") + } + } + } +} + +// TestLengthTable checks that the values precomputed by length give the +// scores of the direct computation, in every mode. +func TestLengthTable(t *testing.T) { + rng := rand.New(rand.NewSource(33)) + entries := randomEntries(rng, 120) + for i := 0; i < 20; i++ { // some very short sequences, which skip the filter + s := entries[i].seq[:8+rng.Intn(10)] + entries[i] = entry{seq: s, length: len(s), count: 1, table: buildTable4(s)} + } + lmax := 0 + for i := range entries { + lmax = max(lmax, entries[i].length) + } + var buf obialign.BandedLCSBuffer + for _, ref := range []reference{refAlignment, refLongest, refShortest} { + for _, mode := range []scoring{ + {threshold: 0.9, normalize: true, lcsmode: true}, + {threshold: 0.8, normalize: true, lcsmode: false}, + {threshold: 30, normalize: false, lcsmode: true}, + {threshold: 6, normalize: false, lcsmode: false}, + } { + direct := mode + direct.ref, direct.ratio = ref, 3 + table := direct + table.prepareLengths(lmax) + if (ref == refShortest) != (table.byLength == nil) { + t.Fatalf("ref %v: table presence is wrong", ref) + } + for i := range entries { + for j := range entries { + a, b := direct.pairScore(&entries[i], &entries[j], -1, &buf), table.pairScore(&entries[i], &entries[j], -1, &buf) + if a != b { + t.Fatalf("ref %v mode %+v pair %d,%d: %v with the table, %v directly", ref, mode, i, j, b, a) + } + } + } + } + } +} + +// TestBlockCommon checks the sums of a block of transposed tables against +// commonWords. +func TestBlockCommon(t *testing.T) { + width := simdBlockWidth() + if width == 0 { + t.Skip("blocks of tables are used with GOEXPERIMENT=simd only") + } + rng := rand.New(rand.NewSource(8)) + seq := func(n int, alpha string) []byte { + s := make([]byte, n) + for i := range s { + s[i] = alpha[rng.Intn(len(alpha))] + } + return s + } + for it := 0; it < 200; it++ { + alpha := []string{"acgt", "ac", "a"}[it%3] + block := newTableBlock(width) + tabs := make([]*table4, width) + for j := range tabs { + tabs[j] = buildTable4(seq(rng.Intn(600), alpha)) + block.set(width, j, tabs[j]) + } + q := buildTable4(seq(rng.Intn(260), alpha)) + words := sparseWords(q) + if words == nil { + continue + } + out := make([]uint8, 64) + block.common(width, words, out) + for j, tab := range tabs { + want := commonWordsRef(q, tab) - min(q.overflow, tab.overflow) + if int(out[j]) != want { + t.Fatalf("lane %d: %d, want %d", j, out[j], want) + } + } + } +} diff --git a/pkg/obitools/obiclust/options.go b/pkg/obitools/obiclust/options.go index ea4f6d0..00a4217 100644 --- a/pkg/obitools/obiclust/options.go +++ b/pkg/obitools/obiclust/options.go @@ -3,102 +3,107 @@ package obiclust import ( "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obitools/obiconvert" "github.com/DavidGamba/go-getoptions" + log "github.com/sirupsen/logrus" ) -type ScoreNormalizationMode int +// reference is the length used to normalize a score (or to express a raw +// distance): the alignment length, the longest or the shortest sequence. +type reference int const ( - NoNormalization ScoreNormalizationMode = iota - NormalizedByShortest - NormalizedByLongest - NormalizedByAlignment + refAlignment reference = iota + refLongest + refShortest ) -var _sampleAttribute = "sample" -var _threshold = 3.0 -var _shortReference = false -var _longReference = false -var _alignReference = true -var _normalizedScore = false -var _distanceMode = false -var _exactMode = false -var _ratioMax = 1.0 -var _sorted_by_length = false -var _sorted_by_abundance = true -var _sortingAscending = false -var _onlyHead = false -var _saveGraph = "__@@NOSAVE@@__" -var _saveRatio = "__@@NOSAVE@@__" -var _minSample = 1 +var _threshold = 0.97 +var _ratio = 1.0 +var _exact = false +var _distance = false +var _raw = false +var _normalized = false +var _refShortest = false +var _refLongest = false +var _refAlignment = false +var _sortKey = "count" +var _ascending = false +var _acceptAmbiguous = false +var _otuMap = "" +var _noSequenceOutput = false func ObiclustOptionSet(options *getoptions.GetOpt) { - options.StringVar(&_sampleAttribute, "sample", _sampleAttribute, - options.Alias("s"), - options.Description("Attribute containing sample descriptions (default %s).")) - - options.Float64Var(&_threshold, "distance", _threshold, - options.Alias("--threshold"), - options.Description("Threshold to merge sequences into the same cluster (default: %d).")) - - options.Float64Var(&_ratioMax, "ratio", _ratioMax, - options.Alias("r"), - options.Description("Threshold ratio between counts (rare/abundant counts)"+ - " of two sequence records so that the less abundant one is a variant of "+ - "the more abundant (default: %3.2f).")) - - options.BoolVar(&_onlyHead, "head", _onlyHead, - options.Alias("H"), - options.Description("Select only sequences with the head status in at least one sample."), + options.Float64Var(&_threshold, "threshold", _threshold, + options.Alias("t"), + options.ArgName("THRESHOLD"), + options.Description("Score threshold for clustering. If the score is normalized and expressed in similarity (default), "+ + "it is an identity, e.g. 0.95 for an identity of 95%. If the score is normalized and expressed in distance, "+ + "it is (1.0 - identity), e.g. 0.05 for an identity of 95%. If the score is not normalized and expressed in similarity, "+ + "it is the length of the Longest Common Subsequence. If the score is not normalized and expressed in distance, "+ + "it is (reference length - LCS length)."), ) - options.StringVar(&_saveGraph, "save-graph", _saveGraph, - options.Description("Creates a directory containing the set of DAG used by the obiclean clustering algorithm. "+ - "The graph files follow the graphml format."), - ) + options.BoolVar(&_refShortest, "shortest", _refShortest, + options.Alias("l"), + options.Description("Reference sequence length is the shortest.")) - options.StringVar(&_saveRatio, "save-ratio", _saveRatio, - options.Description("Creates a file containing the set of abundance ratio on the graph edge. "+ - "The ratio file follows the csv format."), - ) + options.BoolVar(&_refLongest, "longest", _refLongest, + options.Alias("L"), + options.Description("Reference sequence length is the longest.")) - options.IntVar(&_minSample, "min-sample-count", _minSample, - options.Description("Minimum number of samples a sequence must be present in to be considered in the analysis."), - ) + options.BoolVar(&_refAlignment, "alignment", _refAlignment, + options.Alias("a"), + options.Description("Reference sequence length is the alignment length (default).")) - options.BoolVar(&_normalizedScore, "normalized-score", _normalizedScore, + options.BoolVar(&_normalized, "normalized", _normalized, options.Alias("n"), - options.Description("Use alignment score normalized by length"), - ) + options.Description("Score is normalized by the reference sequence length (default).")) - options.BoolVar(&_shortReference, "shortest", _shortReference, - options.Description("Use length of the shortest sequence to normalize alignment score."), - ) + options.BoolVar(&_raw, "raw", _raw, + options.Alias("r"), + options.Description("Raw score, not normalized.")) - options.BoolVar(&_longReference, "longest", _longReference, - options.Description("Use length of the longest sequence to normalize alignment score."), - ) + options.BoolVar(&_distance, "distance", _distance, + options.Alias("d"), + options.Description("Score is expressed in distance (default: score is expressed in similarity).")) - options.BoolVar(&_alignReference, "alignment", _alignReference, - options.Description("Use alignment length to normalize alignment score."), - ) + options.BoolVar(&_exact, "exact", _exact, + options.Alias("e"), + options.Description("A sequence is assigned to the cluster whose center presents the highest score "+ + "above the threshold, as opposed to the default 'fast' mode where it is assigned to the first "+ + "cluster found whose center presents a score above the threshold.")) - options.BoolVar(&_distanceMode, "distance", _distanceMode, - options.Description("Use alignment distance instead of similarity score."), - ) + options.Float64Var(&_ratio, "ratio", _ratio, + options.Alias("R"), + options.ArgName("RATIO"), + options.Description("Maximum ratio between the counts of two sequences so that the less abundant one "+ + "can be considered as a variant of the more abundant one (default: %3.2f).")) - options.BoolVar(&_exactMode, "exact", _exactMode, - options.Description("Use exact clustering algorithm (default is greedy)."), - ) + options.StringVar(&_sortKey, "sort", _sortKey, + options.Alias("s"), + options.ArgName("KEY"), + options.Description("Sort the sequences on KEY before clustering. KEY must be 'None' for no sorting, "+ + "or an attribute of the sequences, except for the count that can be computed (default: sorting on count). "+ + "The sequences are sorted in decreasing order (see --ascending), ties being ordered "+ + "as in the input.")) - options.BoolVar(&_sorted_by_length, "length-ordered", _sorted_by_length, - options.Description("Sort sequence by length before clustering."), - ) - options.BoolVar(&_sorted_by_abundance, "abundance-ordered", _sorted_by_abundance, - options.Description("Sort sequence by read counts (abundance) before clustering."), - ) - options.BoolVar(&_sortingAscending, "ascending-sorting", _sortingAscending, - options.Description("Sort order is ascending (default is descending)."), - ) + options.BoolVar(&_ascending, "ascending", _ascending, + options.Description("Sorting is in ascending order (default: descending).")) + + options.BoolVar(&_acceptAmbiguous, "accept-ambiguous", _acceptAmbiguous, + options.Alias("g"), + options.Description("Accept the sequences containing IUPAC letters other than a, c, g and t. "+ + "Such a sequence can be attached to a cluster (the alignment handles the IUPAC codes), "+ + "but cannot be the center of a cluster, and is left without cluster if it matches no center "+ + "(default: these sequences are discarded).")) + + options.StringVar(&_otuMap, "otu-map", _otuMap, + options.ArgName("FILENAME"), + options.Description("Write the clusters to FILENAME as a CSV table, one row per sequence "+ + "(id, cluster, cluster_center, cluster_score, count), grouped by cluster.")) + + options.BoolVar(&_noSequenceOutput, "no-sequence-output", _noSequenceOutput, + options.Alias("f"), + options.Description("Do not write the sequences on the standard output.")) } func OptionSet(options *getoptions.GetOpt) { @@ -107,44 +112,63 @@ func OptionSet(options *getoptions.GetOpt) { ObiclustOptionSet(options) } -// It returns the name of the attibute used to store sample name -func CLISampleAttribute() string { - return _sampleAttribute -} +// CLIThreshold returns the clustering threshold as given on the command line. +func CLIThreshold() float64 { return _threshold } -func CLIThreshold() float64 { - return _threshold -} +// CLIRatio returns the maximum abundance ratio between a sequence and the center it is attached to. +func CLIRatio() float64 { return _ratio } -// Returns true it the obliclean graphs must be saved -func CLISaveGraphToFiles() bool { - return _saveGraph != "__@@NOSAVE@@__" -} +// CLIExact returns true when a sequence must be attached to the best center and not to the first matching one. +func CLIExact() bool { return _exact } -// It returns the directory where the graph files are saved -func CLIGraphFilesDirectory() string { - return _saveGraph -} +// CLIDistance returns true when the score is expressed in distance. +func CLIDistance() bool { return _distance } -// Returns true it the table of ratio must be saved -func CLIIsSaveRatioTable() bool { - return _saveRatio != "__@@NOSAVE@@__" -} - -// It returns the filename of the file that stores the ratio table -func CLIRatioTableFilename() string { - return _saveRatio -} - -func CLINormalizationMode() ScoreNormalizationMode { - switch { - case _alignReference: - return NormalizedByAlignment - case _longReference: - return NormalizedByLongest - case _shortReference: - return NormalizedByShortest - default: - return NoNormalization +// CLINormalized returns true when the score is normalized by the reference length. +func CLINormalized() bool { + if _raw && _normalized { + log.Fatal("The options --raw and --normalized are exclusive") } + return !_raw } + +// CLIReference returns the length used as reference to normalize the score. +func CLIReference() reference { + n := 0 + ref := refAlignment + if _refShortest { + n++ + ref = refShortest + } + if _refLongest { + n++ + ref = refLongest + } + if _refAlignment { + n++ + } + if n > 1 { + log.Fatal("The options --shortest, --longest and --alignment are exclusive") + } + return ref +} + +// CLISortKey returns the key used to sort the sequences, or "None". +func CLISortKey() string { + if _sortKey == "none" { + return "None" + } + return _sortKey +} + +// CLIAscending returns true when the sort must be in ascending order. +func CLIAscending() bool { return _ascending } + +// CLIAcceptAmbiguous returns true when the sequences with IUPAC letters other than acgt are kept. +func CLIAcceptAmbiguous() bool { return _acceptAmbiguous } + +// CLIOTUMap returns the name of the OTU map file, empty if none is requested. +func CLIOTUMap() string { return _otuMap } + +// CLINoSequenceOutput returns true when the sequences must not be written. +func CLINoSequenceOutput() bool { return _noSequenceOutput } diff --git a/pkg/obitools/obiclust/output.go b/pkg/obitools/obiclust/output.go new file mode 100644 index 0000000..15084f1 --- /dev/null +++ b/pkg/obitools/obiclust/output.go @@ -0,0 +1,128 @@ +package obiclust + +import ( + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiitercsv" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiseq" +) + +// Annotate writes the result of the clustering on the sequences and returns +// them in the order sumaclust writes them: the groups of identical sequences +// in their sorted order, each group beginning with its head. +// +// Every sequence receives +// - cluster: the identifier of the center of its cluster; +// - cluster_score: the score with this center; +// - cluster_center: true for the center only; +// - cluster_weight: the cumulated count of the sequences of the cluster. +// +// A sequence with IUPAC letters that matches no center belongs to no cluster: +// it only gets cluster_center=false. +func (res *Result) Annotate() obiseq.BioSequenceSlice { + recs, groups, cl := res.recs, res.groups, res.clustered + weight := make([]int, len(groups)) + for g := range groups { + if cl.center[g] >= 0 { + weight[cl.center[g]] += groups[g].count + } + } + + out := make(obiseq.BioSequenceSlice, 0, len(recs)) + for g, grp := range groups { + ctr := cl.center[g] + + for r := grp.start; r < grp.end; r++ { + s := recs[r].seq + if ctr < 0 { + // a sequence with IUPAC letters that matches no center + s.RemoveAttribute("cluster") + s.RemoveAttribute("cluster_score") + s.RemoveAttribute("cluster_weight") + s.SetAttribute("cluster_center", false) + out = append(out, s) + continue + } + centerID := recs[groups[ctr].start].seq.Id() + s.SetAttribute("cluster", centerID) + s.SetAttribute("cluster_score", cl.score[g]) + s.SetAttribute("cluster_center", ctr == g && r == grp.start) + s.SetAttribute("cluster_weight", weight[ctr]) + out = append(out, s) + } + } + return out +} + +// OTUMapIterator describes the clusters as a CSV table, one row per sequence: +// - id: the identifier of the sequence; +// - cluster: the identifier of the center of its cluster; +// - cluster_center: true for the center only; +// - cluster_score: the score with this center; +// - count: the count of the sequence. +// +// The rows are grouped by cluster, in the order the clusters were created. In +// a cluster, the center comes first, followed by the sequences identical to +// it, then by the other members, each followed by its identical sequences. +// The sequences without cluster (see Annotate) come last, with no cluster nor +// score. +func (res *Result) OTUMapIterator(source string, batchSize int) *obiitercsv.ICSVRecord { + recs, groups, cl := res.recs, res.groups, res.clustered + + iterator := obiitercsv.NewICSVRecord() + iterator.Add(1) + for _, field := range []string{"id", "cluster", "cluster_center", "cluster_score", "count"} { + iterator.AppendField(field) + } + + members := make(map[int][]int, len(cl.centers)) + var orphans []int + for g := range groups { + if ctr := cl.center[g]; ctr >= 0 { + members[ctr] = append(members[ctr], g) + } else { + orphans = append(orphans, g) + } + } + + go func() { + iterator.WaitAndClose() + }() + + go func() { + order := 0 + batch := make([]obiitercsv.CSVRecord, 0, batchSize) + add := func(g int) { + ctr := cl.center[g] + for r := groups[g].start; r < groups[g].end; r++ { + row := obiitercsv.CSVRecord{ + "id": recs[r].seq.Id(), + "cluster_center": ctr == g && r == groups[g].start, + "count": recs[r].count, + } + if ctr >= 0 { + row["cluster"] = recs[groups[ctr].start].seq.Id() + row["cluster_score"] = cl.score[g] + } + batch = append(batch, row) + if len(batch) == batchSize { + iterator.Push(obiitercsv.MakeCSVRecordBatch(source, order, batch)) + order++ + batch = make([]obiitercsv.CSVRecord, 0, batchSize) + } + } + } + for _, ctr := range cl.centers { + for _, g := range members[ctr] { + add(g) + } + } + for _, g := range orphans { + add(g) + } + if len(batch) > 0 || order == 0 { + iterator.Push(obiitercsv.MakeCSVRecordBatch(source, order, batch)) + } + iterator.Done() + }() + + return iterator +} diff --git a/pkg/obitools/obiclust/prepare.go b/pkg/obitools/obiclust/prepare.go new file mode 100644 index 0000000..73a0f7a --- /dev/null +++ b/pkg/obitools/obiclust/prepare.go @@ -0,0 +1,178 @@ +package obiclust + +import ( + "bytes" + "sort" + "strconv" + "strings" + + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiseq" + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiutils" + log "github.com/sirupsen/logrus" +) + +// record is a sequence of the dataset. +type record struct { + seq *obiseq.BioSequence + pos int // position in the dataset + clean []byte // the nucleotides of the sequence, in lower case + count int + key float64 // value of the sorting key + ambiguous bool // contains IUPAC letters other than a, c, g and t +} + +// group is a run of identical sequences: recs[start] is its head, the +// following ones are the duplicates. count is the cumulated count, key the +// value of the sorting key of the head. +type group struct { + start, end int + count int + key float64 +} + +// nucleotide classes of a byte +const ( + notNucleotide = iota + plainNucleotide + ambiguousNucleotide +) + +var nucleotideClass = func() (t [256]uint8) { + for _, c := range "acgt" { + t[c] = plainNucleotide + } + for _, c := range "umrwsykvhdbn" { + t[c] = ambiguousNucleotide + } + return t +}() + +// classify returns the nucleotides of a sequence and tells if it contains +// IUPAC letters other than a, c, g and t. nil is returned if the sequence is +// empty, or contains anything else than IUPAC letters (and also IUPAC letters +// other than a, c, g and t unless ambiguous ones are accepted). +func classify(s []byte, acceptAmbiguous bool) (clean []byte, ambiguous bool) { + if len(s) == 0 { + return nil, false + } + for _, c := range s { + switch nucleotideClass[c] { + case notNucleotide: + return nil, false + case ambiguousNucleotide: + if !acceptAmbiguous { + return nil, false + } + ambiguous = true + } + } + return s, ambiguous +} + +// atof reads the leading number of a string as C atof does. +func atof(v interface{}) float64 { + if s, ok := v.(string); ok { + s = strings.TrimSpace(s) + for n := len(s); n > 0; n-- { + if f, err := strconv.ParseFloat(s[:n], 64); err == nil { + return f + } + } + return 0 + } + f, err := obiutils.InterfaceToFloat64(v) + if err != nil { + return 0 + } + return f +} + +// sortKeyOf returns the value of the sorting key of a sequence. +func sortKeyOf(s *obiseq.BioSequence, key string) float64 { + if key == "count" { + return float64(s.Count()) + } + v, ok := s.GetAttribute(key) + if !ok { + log.Fatalf("'%s' is not an attribute of the sequence %s", key, s.Id()) + } + return atof(v) +} + +// prepare reads the sequences, groups the identical ones and orders them. +// +// The order is fully determined, whatever the sorting algorithm: +// +// 1. the records of a group of identical sequences are ordered on the +// sorting key (decreasing, increasing if ascending), then on their +// position in the dataset; the first one is the head of the group, whose +// identifier names the cluster if it is a center; +// 2. the groups are ordered on the sorting key of their head (the count of +// the whole group if the key is "count"), then on the position of the +// head in the dataset. With the key "None" they are in the order of the +// first occurrence of their sequence. +// +// The sequences containing anything else than a, c, g and t are discarded, +// except if acceptAmbiguous is set and they only contain IUPAC letters: they +// are then kept, but cannot be the center of a cluster. +func prepare(db obiseq.BioSequenceSlice, key string, ascending, acceptAmbiguous bool) ([]record, []group, int) { + recs := make([]record, 0, len(db)) + discarded := 0 + + for pos, s := range db { + clean, ambiguous := classify(s.Sequence(), acceptAmbiguous) + if clean == nil { + discarded++ + continue + } + r := record{seq: s, pos: pos, clean: clean, count: s.Count(), ambiguous: ambiguous} + if key != "None" { + r.key = sortKeyOf(s, key) + } + recs = append(recs, r) + } + + // before reports whether a is ranked before b by the sorting key + before := func(a, b float64) bool { + if ascending { + return a < b + } + return a > b + } + + sort.Slice(recs, func(i, j int) bool { + a, b := &recs[i], &recs[j] + if c := bytes.Compare(a.clean, b.clean); c != 0 { + return c < 0 + } + if a.key != b.key && key != "None" { + return before(a.key, b.key) + } + return a.pos < b.pos + }) + + groups := make([]group, 0, len(recs)) + for i := 0; i < len(recs); { + j, count := i+1, recs[i].count + for j < len(recs) && bytes.Equal(recs[j].clean, recs[i].clean) { + count += recs[j].count + j++ + } + g := group{start: i, end: j, count: count, key: recs[i].key} + if key == "count" { + g.key = float64(count) + } + groups = append(groups, g) + i = j + } + + sort.Slice(groups, func(i, j int) bool { + a, b := &groups[i], &groups[j] + if key != "None" && a.key != b.key { + return before(a.key, b.key) + } + return recs[a.start].pos < recs[b.start].pos + }) + + return recs, groups, discarded +} diff --git a/pkg/obitools/obiclust/score.go b/pkg/obitools/obiclust/score.go new file mode 100644 index 0000000..5d13a08 --- /dev/null +++ b/pkg/obitools/obiclust/score.go @@ -0,0 +1,233 @@ +package obiclust + +import ( + "math" + + "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obialign" + log "github.com/sirupsen/logrus" +) + +// entry is a distinct sequence of the dataset with what the clustering needs. +type entry struct { + seq []byte + length int + count int // cumulated count of the identical sequences + table *table4 + sparse []uint16 // words of the table (see sparseWords), nil if unusable + // ambiguous sequences contain IUPAC letters other than a, c, g and t: + // they have no 4-mer table, and cannot be the center of a cluster + ambiguous bool +} + +// scoring holds the way sumaclust scores a pair of sequences and decides +// whether it passes the threshold. It reproduces the arithmetic of the +// reference implementation (integer truncations, floating point +// comparisons) so that the same pairs are accepted with the same scores. +type scoring struct { + threshold float64 // threshold as used for the comparison (1 - t for a normalized distance) + normalize bool + lcsmode bool // true: score is a similarity, false: it is a distance + ref reference + ratio float64 + worst float64 // score of a pair that cannot be attached + + // byLength gives, for the pairs whose longest sequence has a length l, + // the values that depend on l only. It is built when the reference is not + // the shortest sequence, otherwise those values depend on both lengths. + byLength []lengthInfo +} + +type lengthInfo struct { + lcsMin int32 // see lcsMin + minCommon int32 // see minCommon +} + +// prepareLengths fills byLength for the lengths up to lmax. +func (sc *scoring) prepareLengths(lmax int) { + if sc.ref == refShortest { + return + } + sc.byLength = make([]lengthInfo, lmax+1) + for l := range sc.byLength { + sc.byLength[l] = lengthInfo{int32(sc.lcsMin(l, l)), int32(sc.minCommon(l, l))} + } +} + +// similarityLike tells if the comparison with the threshold is "score >= threshold". +func (sc *scoring) similarityLike() bool { return sc.lcsmode || sc.normalize } + +// lcsMin is the smallest LCS score that a pair of lengths l1 >= l2 can have +// and still pass the threshold (calculateLCSmin of sumaclust). +func (sc *scoring) lcsMin(l1, l2 int) int { + switch { + case sc.threshold <= 0: + return 0 + case sc.normalize: + if sc.ref == refShortest { + return int(sc.threshold * float64(l2)) + } + return int(sc.threshold * float64(l1)) + case sc.lcsmode: + return int(sc.threshold) + case sc.ref == refShortest: + return int(float64(l2) - sc.threshold) + default: + return int(float64(l1) - sc.threshold) + } +} + +// minCommon is the number of 4-mers that two sequences of lengths l1 >= l2 +// must share to be possibly similar enough (isPossibleSumathings). +func (sc *scoring) minCommon(l1, l2 int) int { + reflen := l2 + if sc.ref == refAlignment || sc.ref == refLongest { + reflen = l1 + } + var lcs int + if sc.normalize { + lcs = int(math.Ceil(float64(reflen) * sc.threshold)) + } else { + th := sc.threshold + if !sc.lcsmode { + th = float64(reflen) - th + } + lcs = int(th) + } + return minCommonWords(l1, lcs) +} + +// pairScore returns the score of a query sequence against a center, or the +// worst score when the pair cannot be attached. +// +// common, if not negative, is the sum of the minima of the 4-mer counters of +// the two sequences, already computed by a block of tables. +func (sc *scoring) pairScore(q, c *entry, common int, buf *obialign.BandedLCSBuffer) float64 { + // The quotient of two counts is not above 1 when the first is not above + // the second, which spares the division in the usual case. + if !(sc.ratio >= 1 && q.count <= c.count) && float64(q.count)/float64(c.count) > sc.ratio { + return sc.worst + } + + l1, l2 := q.length, c.length + long, short := q, c + if l1 < l2 { + l1, l2 = l2, l1 + long, short = c, q + } + var lcsMin, minCommon int + if sc.byLength != nil { + info := sc.byLength[l1] + lcsMin, minCommon = int(info.lcsMin), int(info.minCommon) + } else { + lcsMin, minCommon = sc.lcsMin(l1, l2), sc.minCommon(l1, l2) + } + if l2 < lcsMin { + return sc.worst + } + // The 4-mer filter counts identical words: it does not hold when IUPAC + // codes can match different letters. It lets short sequences pass. + if l1 >= 12 && l2 >= 12 && !long.ambiguous && !short.ambiguous { + if common >= 0 { + common += min(long.table.overflow, short.table.overflow) + } else { + common = commonWords(long.table, short.table) + } + if common < minCommon { + return sc.worst + } + } + + // The length of the alignment is needed only to normalize by it, or to + // express a distance with it; the score alone is much cheaper. + var lcs, alilen int + var err error + if sc.ref == refAlignment && (sc.normalize || !sc.lcsmode) { + lcs, alilen, err = buf.AtLeast(q.seq, c.seq, lcsMin) + } else { + lcs, err = buf.ScoreAtLeast(q.seq, c.seq, lcsMin) + } + if err != nil { + log.Fatalf("Cannot align two sequences: %v", err) + } + if lcs < 0 { + // The best alignment has a score under lcsMin: the pair cannot pass. + return sc.worst + } + + id := float64(lcs) + if !sc.lcsmode && !sc.normalize { + switch sc.ref { + case refAlignment: + id = float64(alilen) - id + case refLongest: + id = float64(l1) - id + case refShortest: + id = float64(l2) - id + } + } + if sc.normalize { + switch sc.ref { + case refAlignment: + id /= float64(alilen) + case refLongest: + id /= float64(l1) + case refShortest: + id /= float64(l2) + } + } + return id +} + +// beats tells if the score s is better than the score best (without any +// threshold). +func (sc *scoring) beats(s, best float64) bool { + if sc.similarityLike() { + return s > best + } + return s < best +} + +// lcsMinFor is lcsMin for sequences of lengths l1 >= l2, from the table when it exists. +func (sc *scoring) lcsMinFor(l1, l2 int) int { + if sc.byLength != nil { + return int(sc.byLength[l1].lcsMin) + } + return sc.lcsMin(l1, l2) +} + +// lengthCompatible tells if two sequences of these lengths may be similar enough: +// the shortest cannot be shorter than the minimum LCS score of the pair. +func (sc *scoring) lengthCompatible(la, lb int) bool { + l1, l2 := max(la, lb), min(la, lb) + return l2 >= sc.lcsMinFor(l1, l2) +} + +// selects tells if a pair of score s must replace the best pair found so far, +// of score best. +func (sc *scoring) selects(s, best float64) bool { + if sc.similarityLike() { + return s >= sc.threshold && s > best + } + return s <= sc.threshold && s < best +} + +// centerScore is the score of a sequence with itself. +func (sc *scoring) centerScore(length int) float64 { + switch { + case sc.normalize && sc.lcsmode: + return 1.0 + case !sc.lcsmode: + return 0.0 + default: + return float64(length) + } +} + +// memberScore is the score reported for a sequence attached to a center +// with the score s. +func (sc *scoring) memberScore(s float64) float64 { + if !sc.lcsmode && sc.normalize { + return 1.0 - s + } + return s +} diff --git a/pkg/obitools/obiclust/testdata/cases.txt b/pkg/obitools/obiclust/testdata/cases.txt new file mode 100644 index 0000000..5e543da --- /dev/null +++ b/pkg/obitools/obiclust/testdata/cases.txt @@ -0,0 +1,9 @@ +families default -t 0.9 +families exact -t 0.9 -e +families shortest -t 0.9 -l -e +families longest -t 0.85 -L +families distance -t 0.1 -d +families rawdist -t 4 -r -d -e +families rawlcs -t 40 -r +families ratio -t 0.9 -R 0.5 +families ascending -t 0.9 -o diff --git a/pkg/obitools/obiclust/testdata/families_ascending.expected b/pkg/obitools/obiclust/testdata/families_ascending.expected new file mode 100644 index 0000000..dfd6028 --- /dev/null +++ b/pkg/obitools/obiclust/testdata/families_ascending.expected @@ -0,0 +1,101 @@ +f5v2s55 f5v2s55 1.000000 T 666 cggcatcgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccact +f1v6s15 f1v6s15 1.000000 T 3070 gaatcagcttctaaccaagcgttgcagaaccggctactttaagcatgatgaatgcgtcgtaagtgatactcgacgattc +f7v2s66 f7v2s66 1.000000 T 3988 gcttcgtgggaaccagaacaaccgacatattccgaaccaccggacatacgatccttaaacc +f4v7s51 f4v7s51 1.000000 T 8599 cgtttattcgtaggatctactacgcatcaaagtatcccacttttgtaccccactaagagcaga +f5v1s54 f5v1s54 1.000000 T 9091 cggaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactt +f7v1s65 f7v1s65 1.000000 T 11279 gcttcggggaaccagagacaaccgacatatttcgaaccaccgacatacgatcctttaaacc +f3v8s39 f3v8s39 1.000000 T 13623 cgaaaaaggtgaatgaaaggcctgggacgctcctggttggggcgctctaggtgtgtggcttgaacgttagacct +f4v4s48 f4v4s48 1.000000 T 15906 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaa +f3v11s42 f3v11s42 1.000000 T 18309 cgaaaaaggtgaatgaaagtctgggacgctcctggtttgggacgctctaggtgttgtggcttgaacgttagcacct +f6v3s63 f6v3s63 1.000000 T 19024 catgtcaagagactggaaccccggtgatgtgaaaaaaggttttgggtcacgtctccatggctacg +f11v3s94 f11v3s94 1.000000 T 21189 agcgttagcaaagagcctggttaatcgccgtcgaaggatcggtggtgatcagagatgcgggcatccgtttc +f2v6s24 f2v6s24 1.000000 T 21984 tggactagtagacgtgtgcctcatcgacgcgtgagacactttgatttcttatacggcgtctcag +f0v8s8 f0v8s8 1.000000 T 24872 ggatcacagtcctacactgctcactccaaccccggccctgagtccgaggagagggtgcttcagagtatgtataccactg +f7v3s67 f7v3s67 1.000000 T 26251 gcttcgtgggaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f2v9s27 f2v9s27 1.000000 T 26719 tggactagtagaccgtgtgcctaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f4v5s49 f4v5s49 1.000000 T 26933 ctatattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaagca +f11v9s100 f11v9s100 1.000000 T 27405 agcgttagcaaaggagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f2v12s30 f2v12s30 1.000000 T 27908 tggactagtagaccgtgtgccacaatcgacgctgaggacacgttgatttttatacggcgtctcag +f9v0s76 f9v0s76 1.000000 T 29993 gggatcgtggcaagcaacccttgccgcctcgactgcggctaaagacgcatgttccagtaataccta +f4v6s50 f4v6s50 1.000000 T 30723 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagacgaagag +f6v2s62 f6v2s62 1.000000 T 33001 catgtcgagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctac +f11v5s96 f11v5s96 1.000000 T 33026 agcgttagcaaagagcctggttaatggccctcgaggatcggtggctgaccagagatgcgggcatccgtttc +f8v1s69 f8v1s69 1.000000 T 33813 aagaccttgaacacctctaagcgaaaattggatatcgagtcagggaacataaccacgggtgcgt +f11v4s95 f11v4s95 1.000000 T 34530 agcgttagcaaagagcctggttaatcccctcgagatcgggtggtgatcagagatgcgggcatccgtttc +f3v9s40 f3v9s40 1.000000 T 35203 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggggttgtggcttgaacgttagcacct +f11v2s93 f11v2s93 1.000000 T 35758 agcgttagcaaagagcctggttaatcgccctcggaggatcggtggtgatcagagatgcgggcatccgttc +f10v6s83 f10v6s83 1.000000 T 36011 ttccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcctgatgggtagc +f3v7s38 f3v7s38 1.000000 T 36703 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttcgtgcttgaacgttagcacct +f3v10s41 f3v10s41 1.000000 T 37492 cgaaaataaggtgaatgaaagggtgggacgctcctagtttggggcgctctaggtgttgtggcttgaacgttagcacct +f5v4s57 f5v4s57 1.000000 T 38613 cggcaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactat +f10v1s78 f10v1s78 1.000000 T 43528 ttgccggacactcgcaatcctccaagcagaggtccagctaacacacagcaggcatttagtcccgatgaggtagc +f10v8s85 f10v8s85 1.000000 T 46704 ttgccggacactcgaatcttccaagcagaggtccagctaacacacagcaggcatactagtcccgatgggtagc +f6v0s60 f6v0s60 1.000000 T 48958 catgtcaagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctacg +f8v6s74 f8v6s74 1.000000 T 49062 aagacccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgccgt +f1v0s9 f1v0s9 1.000000 T 49791 gaatcagcttctaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactcgacgattc +f5v5s58 f5v5s58 1.000000 T 49852 cgcaacgcaccattaagatggtaatcactaaaaaaatcggcctaacctgtccaatcgccactt +f0v5s5 f0v5s5 1.000000 T 54120 ggatcacagtctacactgctcactcccccccggcccctgagtccgaggagagggtgcttcaaagtatgtataccactg +f4v1s45 f4v1s45 1.000000 T 55908 ctgtatttcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactatagagaaga +f11v6s97 f11v6s97 1.000000 T 58908 agcgttagcaaagaacctggttaatcgccctcgaggatcggtggtggatcggagatgcgggcatcgcgtttc +f3v4s35 f3v4s35 1.000000 T 59170 cgaaaaaggtgaagaaaggctgggacgctcctggttggcgcgctctaggtgttgtggcttgaacgttagcaccta +f3v1s32 f3v1s32 1.000000 T 59632 cgaaaaagagtgaatgaaatggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f8v4s72 f8v4s72 1.000000 T 59724 aagacttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f8v5s73 f8v5s73 1.000000 T 64084 aagaccttgtacacctctaagcgataaattggattcgagtcagggaacataaccacggatgcgt +f11v0s91 f11v0s91 1.000000 T 66160 agcgttagcaaagagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f10v10s87 f10v10s87 1.000000 T 66794 tctgccggacactcgcaatcttccaagcagaggtccagctaacacacgcaggcatttatccgatgggtagc +f4v3s47 f4v3s47 1.000000 T 69396 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaaagaaga +f8v3s71 f8v3s71 1.000000 T 70497 aagaccttgaacacctctaagcgaaattggattcgagtcagcgaacataacacgggtgcgt +f3v2s33 f3v2s33 1.000000 T 70860 cgaaaaaaggtaatgaaaggctgggatgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcact +f11v1s92 f11v1s92 1.000000 T 71853 agcgttagcaaagagcctggttcatcgccctcgaggatcggtggtgatcaagagatgcggcatccgtttc +f7v0s64 f7v0s64 1.000000 T 72498 gcttcgtgggaaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f11v8s99 f11v8s99 1.000000 T 72674 agcgttagcaaagagcctggttaatctgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f8v2s70 f8v2s70 1.000000 T 74088 aagaccttgaacaacctctaagccgaaattggaattcgagtcagggaacataaccacgggtgcgat +f4v2s46 f4v2s46 1.000000 T 74433 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaga +f10v12s89 f10v12s89 1.000000 T 74438 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacacgcaggcatttagacccgatgggtagc +f8v7s75 f8v7s75 1.000000 T 75803 aagaccttgaacacctctaagcgaaaattggattccagtcagggaaccataaccacgggtgcgt +f3v5s36 f3v5s36 1.000000 T 76948 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttatggcttgaaagttagcacct +f10v11s88 f10v11s88 1.000000 T 78894 ttgccggacactcgcaatcttccaagcagaggtccagctaactcacagccaggcatttagtcccgatgggtagc +f1v8s17 f1v8s17 1.000000 T 79492 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f2v1s19 f1v8s17 1.000000 F 79492 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f8v0s68 f8v0s68 1.000000 T 80207 aagaccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f3v12s43 f3v12s43 1.000000 T 80835 cgaaaaaggtgaatgaaagactgggacgcctcctggtttgggacgctctaggtggttgtggcttgaacgttagcacct +f2v5s23 f2v5s23 1.000000 T 81657 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f1v7s16 f2v5s23 1.000000 F 81657 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f0v2s2 f0v2s2 1.000000 T 83299 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f0v0s0 f0v2s2 1.000000 F 83299 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f1v3s12 f1v3s12 1.000000 T 84418 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f0v6s6 f1v3s12 1.000000 F 84418 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f2v11s29 f2v11s29 1.000000 T 85592 tggactgtagaaccgtgtgcctcaatccacgcgtgaggacacgttgatttcttatacggcgtctcag +f4v8s52 f4v8s52 1.000000 T 86639 ctgatattcgtattgatctactacgtatcaaagtatcccacttttgtgaccccactaagagaaga +f10v5s82 f10v5s82 1.000000 T 87843 ttgccggacactcgcaatcttccaagcagaggtccagctaacacatagcaggcatttagtcccgatgggtagc +f10v4s81 f10v4s81 1.000000 T 88973 ttgccggacactggcaatcttccatagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f1v4s13 f1v4s13 1.000000 T 90139 gaatcagcttctcaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactggacgattc +f10v13s90 f10v13s90 1.000000 T 92493 tgccggacactcgcaatctccaagcagagagtccaagctaacacacagcaggcatttgtcccgatgggtagc +f10v2s79 f10v2s79 1.000000 T 93964 ttgccggtcactcgcaatcttaccaaccagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f5v3s56 f5v3s56 1.000000 T 94426 ccgcaacgcaccataagatgtaatcactaagaaaaatcggcctaacctgtcatatcgccactt +f0v3s3 f0v3s3 1.000000 T 95466 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v1s1 f0v3s3 1.000000 F 95466 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v4s4 f0v3s3 1.000000 F 95466 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f1v5s14 f0v3s3 1.000000 F 95466 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f2v10s28 f2v10s28 1.000000 T 97350 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f5v6s59 f2v10s28 1.000000 F 97350 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v0s77 f10v0s77 1.000000 T 97674 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v7s84 f10v7s84 1.000000 T 98678 gtgccggacactcgcaatcttcacaagcagaggtccagctaacacacagcagcatttagtcccgatgggtagc +f2v4s22 f2v4s22 1.000000 T 99661 tggactagtagaccgtgtcgcctcaatcgacgcgtgaggacacgttgattcttatacggcgtctcag +f11v7s98 f11v7s98 1.000000 T 167833 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f5v0s53 f11v7s98 1.000000 F 167833 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f3v6s37 f11v7s98 1.000000 F 167833 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f1v1s10 f1v1s10 1.000000 T 173457 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v2s11 f1v1s10 1.000000 F 173457 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f0v7s7 f1v1s10 1.000000 F 173457 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f3v0s31 f3v0s31 1.000000 T 182106 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f4v0s44 f3v0s31 1.000000 F 182106 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v3s34 f3v0s31 1.000000 F 182106 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f2v2s20 f2v2s20 1.000000 T 243753 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f6v1s61 f2v2s20 1.000000 F 243753 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f2v3s21 f2v2s20 1.000000 F 243753 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f10v9s86 f10v9s86 1.000000 T 260688 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v7s25 f10v9s86 1.000000 F 260688 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v0s18 f10v9s86 1.000000 F 260688 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v3s80 f10v9s86 1.000000 F 260688 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v8s26 f10v9s86 1.000000 F 260688 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag diff --git a/pkg/obitools/obiclust/testdata/families_default.expected b/pkg/obitools/obiclust/testdata/families_default.expected new file mode 100644 index 0000000..fc0c73b --- /dev/null +++ b/pkg/obitools/obiclust/testdata/families_default.expected @@ -0,0 +1,101 @@ +f2v8s26 f2v8s26 1.000000 T 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v3s80 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v0s18 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v7s25 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v9s86 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v3s21 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f6v1s61 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f2v2s20 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f3v3s34 f3v3s34 1.000000 T 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f4v0s44 f3v3s34 1.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v0s31 f3v3s34 1.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f0v7s7 f0v7s7 1.000000 T 281628 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v2s11 f0v7s7 1.000000 F 281628 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v1s10 f0v7s7 1.000000 F 281628 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f3v6s37 f3v3s34 0.935897 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f5v0s53 f3v3s34 0.935897 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f11v7s98 f3v3s34 0.935897 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f2v4s22 f2v8s26 0.970588 F 863655 tggactagtagaccgtgtcgcctcaatcgacgcgtgaggacacgttgattcttatacggcgtctcag +f10v7s84 f10v7s84 1.000000 T 905994 gtgccggacactcgcaatcttcacaagcagaggtccagctaacacacagcagcatttagtcccgatgggtagc +f10v0s77 f10v7s84 0.959459 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f5v6s59 f2v8s26 0.985075 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v10s28 f2v8s26 0.985075 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f1v5s14 f1v5s14 1.000000 T 234004 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v4s4 f1v5s14 1.000000 F 234004 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v1s1 f1v5s14 1.000000 F 234004 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v3s3 f1v5s14 1.000000 F 234004 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f5v3s56 f5v3s56 1.000000 T 192648 ccgcaacgcaccataagatgtaatcactaagaaaaatcggcctaacctgtcatatcgccactt +f10v2s79 f10v7s84 0.920000 F 905994 ttgccggtcactcgcaatcttaccaaccagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v13s90 f10v7s84 0.907895 F 905994 tgccggacactcgcaatctccaagcagagagtccaagctaacacacagcaggcatttgtcccgatgggtagc +f1v4s13 f1v4s13 1.000000 T 304149 gaatcagcttctcaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactggacgattc +f10v4s81 f10v7s84 0.933333 F 905994 ttgccggacactggcaatcttccatagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v5s82 f10v7s84 0.945946 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacatagcaggcatttagtcccgatgggtagc +f4v8s52 f4v8s52 1.000000 T 359938 ctgatattcgtattgatctactacgtatcaaagtatcccacttttgtgaccccactaagagaaga +f2v11s29 f2v8s26 0.955882 F 863655 tggactgtagaaccgtgtgcctcaatccacgcgtgaggacacgttgatttcttatacggcgtctcag +f0v6s6 f1v5s14 0.925000 F 234004 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f1v3s12 f1v5s14 0.925000 F 234004 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f0v0s0 f0v7s7 0.916667 F 281628 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f0v2s2 f0v7s7 0.916667 F 281628 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f1v7s16 f1v4s13 0.913580 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f2v5s23 f1v4s13 0.913580 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f3v12s43 f3v3s34 0.948718 F 838714 cgaaaaaggtgaatgaaagactgggacgcctcctggtttgggacgctctaggtggttgtggcttgaacgttagcacct +f8v0s68 f8v0s68 1.000000 T 507278 aagaccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f2v1s19 f1v4s13 0.902439 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f1v8s17 f1v4s13 0.902439 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f10v11s88 f10v7s84 0.933333 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaactcacagccaggcatttagtcccgatgggtagc +f3v5s36 f3v3s34 0.973684 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttatggcttgaaagttagcacct +f8v7s75 f8v0s68 0.968750 F 507278 aagaccttgaacacctctaagcgaaaattggattccagtcagggaaccataaccacgggtgcgt +f10v12s89 f10v7s84 0.933333 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacacgcaggcatttagacccgatgggtagc +f4v2s46 f4v8s52 0.938462 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaga +f8v2s70 f8v0s68 0.925373 F 507278 aagaccttgaacaacctctaagccgaaattggaattcgagtcagggaacataaccacgggtgcgat +f11v8s99 f11v8s99 1.000000 T 421503 agcgttagcaaagagcctggttaatctgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f7v0s64 f7v0s64 1.000000 T 114016 gcttcgtgggaaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f11v1s92 f11v8s99 0.944444 F 421503 agcgttagcaaagagcctggttcatcgccctcgaggatcggtggtgatcaagagatgcggcatccgtttc +f3v2s33 f3v3s34 0.948052 F 838714 cgaaaaaaggtaatgaaaggctgggatgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcact +f8v3s71 f8v0s68 0.952381 F 507278 aagaccttgaacacctctaagcgaaattggattcgagtcagcgaacataacacgggtgcgt +f4v3s47 f4v8s52 0.923077 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaaagaaga +f10v10s87 f10v7s84 0.906667 F 905994 tctgccggacactcgcaatcttccaagcagaggtccagctaacacacgcaggcatttatccgatgggtagc +f11v0s91 f11v8s99 0.985915 F 421503 agcgttagcaaagagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f8v5s73 f8v0s68 0.953125 F 507278 aagaccttgtacacctctaagcgataaattggattcgagtcagggaacataaccacggatgcgt +f8v4s72 f8v0s68 0.984127 F 507278 aagacttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f3v1s32 f3v3s34 0.974359 F 838714 cgaaaaagagtgaatgaaatggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v4s35 f3v3s34 0.948052 F 838714 cgaaaaaggtgaagaaaggctgggacgctcctggttggcgcgctctaggtgttgtggcttgaacgttagcaccta +f11v6s97 f11v8s99 0.931507 F 421503 agcgttagcaaagaacctggttaatcgccctcgaggatcggtggtggatcggagatgcgggcatcgcgtttc +f4v1s45 f4v8s52 0.910448 F 359938 ctgtatttcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactatagagaaga +f0v5s5 f1v5s14 0.925000 F 234004 ggatcacagtctacactgctcactcccccccggcccctgagtccgaggagagggtgcttcaaagtatgtataccactg +f5v5s58 f5v3s56 0.909091 F 192648 cgcaacgcaccattaagatggtaatcactaaaaaaatcggcctaacctgtccaatcgccactt +f1v0s9 f1v4s13 0.975309 F 304149 gaatcagcttctaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactcgacgattc +f8v6s74 f8v0s68 0.969231 F 507278 aagacccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgccgt +f6v0s60 f6v0s60 1.000000 T 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctacg +f10v8s85 f10v7s84 0.920000 F 905994 ttgccggacactcgaatcttccaagcagaggtccagctaacacacagcaggcatactagtcccgatgggtagc +f10v1s78 f10v7s84 0.933333 F 905994 ttgccggacactcgcaatcctccaagcagaggtccagctaacacacagcaggcatttagtcccgatgaggtagc +f5v4s57 f5v3s56 0.924242 F 192648 cggcaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactat +f3v10s41 f3v3s34 0.948718 F 838714 cgaaaataaggtgaatgaaagggtgggacgctcctagtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v7s38 f3v3s34 0.974026 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttcgtgcttgaacgttagcacct +f10v6s83 f10v7s84 0.932432 F 905994 ttccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcctgatgggtagc +f11v2s93 f11v8s99 0.958333 F 421503 agcgttagcaaagagcctggttaatcgccctcggaggatcggtggtgatcagagatgcgggcatccgttc +f3v9s40 f3v3s34 0.986842 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggggttgtggcttgaacgttagcacct +f11v4s95 f11v8s99 0.944444 F 421503 agcgttagcaaagagcctggttaatcccctcgagatcgggtggtgatcagagatgcgggcatccgtttc +f8v1s69 f8v0s68 0.984375 F 507278 aagaccttgaacacctctaagcgaaaattggatatcgagtcagggaacataaccacgggtgcgt +f11v5s96 f11v8s99 0.944444 F 421503 agcgttagcaaagagcctggttaatggccctcgaggatcggtggctgaccagagatgcgggcatccgtttc +f6v2s62 f6v0s60 0.968254 F 100983 catgtcgagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctac +f4v6s50 f4v8s52 0.910448 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagacgaagag +f9v0s76 f9v0s76 1.000000 T 29993 gggatcgtggcaagcaacccttgccgcctcgactgcggctaaagacgcatgttccagtaataccta +f2v12s30 f2v8s26 0.955224 F 863655 tggactagtagaccgtgtgccacaatcgacgctgaggacacgttgatttttatacggcgtctcag +f11v9s100 f11v8s99 0.972222 F 421503 agcgttagcaaaggagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f4v5s49 f4v8s52 0.924242 F 359938 ctatattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaagca +f2v9s27 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f7v3s67 f7v0s64 0.983607 F 114016 gcttcgtgggaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f0v8s8 f0v7s7 0.905882 F 281628 ggatcacagtcctacactgctcactccaaccccggccctgagtccgaggagagggtgcttcagagtatgtataccactg +f2v6s24 f2v8s26 0.940299 F 863655 tggactagtagacgtgtgcctcatcgacgcgtgagacactttgatttcttatacggcgtctcag +f11v3s94 f11v8s99 0.958333 F 421503 agcgttagcaaagagcctggttaatcgccgtcgaaggatcggtggtgatcagagatgcgggcatccgtttc +f6v3s63 f6v0s60 0.953846 F 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggttttgggtcacgtctccatggctacg +f3v11s42 f3v3s34 0.973684 F 838714 cgaaaaaggtgaatgaaagtctgggacgctcctggtttgggacgctctaggtgttgtggcttgaacgttagcacct +f4v4s48 f4v8s52 0.923077 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaa +f3v8s39 f3v3s34 0.948052 F 838714 cgaaaaaggtgaatgaaaggcctgggacgctcctggttggggcgctctaggtgtgtggcttgaacgttagacct +f7v1s65 f7v0s64 0.967742 F 114016 gcttcggggaaccagagacaaccgacatatttcgaaccaccgacatacgatcctttaaacc +f5v1s54 f5v3s56 0.923077 F 192648 cggaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactt +f4v7s51 f4v7s51 1.000000 T 8599 cgtttattcgtaggatctactacgcatcaaagtatcccacttttgtaccccactaagagcaga +f7v2s66 f7v0s64 0.951613 F 114016 gcttcgtgggaaccagaacaaccgacatattccgaaccaccggacatacgatccttaaacc +f1v6s15 f1v4s13 0.950617 F 304149 gaatcagcttctaaccaagcgttgcagaaccggctactttaagcatgatgaatgcgtcgtaagtgatactcgacgattc +f5v2s55 f5v3s56 0.907692 F 192648 cggcatcgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccact diff --git a/pkg/obitools/obiclust/testdata/families_distance.expected b/pkg/obitools/obiclust/testdata/families_distance.expected new file mode 100644 index 0000000..1b82708 --- /dev/null +++ b/pkg/obitools/obiclust/testdata/families_distance.expected @@ -0,0 +1,101 @@ +f2v8s26 f2v8s26 0.000000 T 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v3s80 f2v8s26 0.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v0s18 f2v8s26 0.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v7s25 f2v8s26 0.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v9s86 f2v8s26 0.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v3s21 f2v8s26 0.014925 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f6v1s61 f2v8s26 0.014925 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f2v2s20 f2v8s26 0.014925 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f3v3s34 f3v3s34 0.000000 T 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f4v0s44 f3v3s34 0.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v0s31 f3v3s34 0.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f0v7s7 f0v7s7 0.000000 T 281628 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v2s11 f0v7s7 0.000000 F 281628 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v1s10 f0v7s7 0.000000 F 281628 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f3v6s37 f3v3s34 0.064103 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f5v0s53 f3v3s34 0.064103 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f11v7s98 f3v3s34 0.064103 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f2v4s22 f2v8s26 0.029412 F 863655 tggactagtagaccgtgtcgcctcaatcgacgcgtgaggacacgttgattcttatacggcgtctcag +f10v7s84 f10v7s84 0.000000 T 905994 gtgccggacactcgcaatcttcacaagcagaggtccagctaacacacagcagcatttagtcccgatgggtagc +f10v0s77 f10v7s84 0.040541 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f5v6s59 f2v8s26 0.014925 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v10s28 f2v8s26 0.014925 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f1v5s14 f1v5s14 0.000000 T 234004 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v4s4 f1v5s14 0.000000 F 234004 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v1s1 f1v5s14 0.000000 F 234004 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v3s3 f1v5s14 0.000000 F 234004 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f5v3s56 f5v3s56 0.000000 T 192648 ccgcaacgcaccataagatgtaatcactaagaaaaatcggcctaacctgtcatatcgccactt +f10v2s79 f10v7s84 0.080000 F 905994 ttgccggtcactcgcaatcttaccaaccagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v13s90 f10v7s84 0.092105 F 905994 tgccggacactcgcaatctccaagcagagagtccaagctaacacacagcaggcatttgtcccgatgggtagc +f1v4s13 f1v4s13 0.000000 T 304149 gaatcagcttctcaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactggacgattc +f10v4s81 f10v7s84 0.066667 F 905994 ttgccggacactggcaatcttccatagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v5s82 f10v7s84 0.054054 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacatagcaggcatttagtcccgatgggtagc +f4v8s52 f4v8s52 0.000000 T 359938 ctgatattcgtattgatctactacgtatcaaagtatcccacttttgtgaccccactaagagaaga +f2v11s29 f2v8s26 0.044118 F 863655 tggactgtagaaccgtgtgcctcaatccacgcgtgaggacacgttgatttcttatacggcgtctcag +f0v6s6 f1v5s14 0.075000 F 234004 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f1v3s12 f1v5s14 0.075000 F 234004 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f0v0s0 f0v7s7 0.083333 F 281628 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f0v2s2 f0v7s7 0.083333 F 281628 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f1v7s16 f1v4s13 0.086420 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f2v5s23 f1v4s13 0.086420 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f3v12s43 f3v3s34 0.051282 F 838714 cgaaaaaggtgaatgaaagactgggacgcctcctggtttgggacgctctaggtggttgtggcttgaacgttagcacct +f8v0s68 f8v0s68 0.000000 T 507278 aagaccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f2v1s19 f1v4s13 0.097561 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f1v8s17 f1v4s13 0.097561 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f10v11s88 f10v7s84 0.066667 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaactcacagccaggcatttagtcccgatgggtagc +f3v5s36 f3v3s34 0.026316 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttatggcttgaaagttagcacct +f8v7s75 f8v0s68 0.031250 F 507278 aagaccttgaacacctctaagcgaaaattggattccagtcagggaaccataaccacgggtgcgt +f10v12s89 f10v7s84 0.066667 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacacgcaggcatttagacccgatgggtagc +f4v2s46 f4v8s52 0.061538 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaga +f8v2s70 f8v0s68 0.074627 F 507278 aagaccttgaacaacctctaagccgaaattggaattcgagtcagggaacataaccacgggtgcgat +f11v8s99 f11v8s99 0.000000 T 421503 agcgttagcaaagagcctggttaatctgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f7v0s64 f7v0s64 0.000000 T 114016 gcttcgtgggaaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f11v1s92 f11v8s99 0.055556 F 421503 agcgttagcaaagagcctggttcatcgccctcgaggatcggtggtgatcaagagatgcggcatccgtttc +f3v2s33 f3v3s34 0.051948 F 838714 cgaaaaaaggtaatgaaaggctgggatgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcact +f8v3s71 f8v0s68 0.047619 F 507278 aagaccttgaacacctctaagcgaaattggattcgagtcagcgaacataacacgggtgcgt +f4v3s47 f4v8s52 0.076923 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaaagaaga +f10v10s87 f10v7s84 0.093333 F 905994 tctgccggacactcgcaatcttccaagcagaggtccagctaacacacgcaggcatttatccgatgggtagc +f11v0s91 f11v8s99 0.014085 F 421503 agcgttagcaaagagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f8v5s73 f8v0s68 0.046875 F 507278 aagaccttgtacacctctaagcgataaattggattcgagtcagggaacataaccacggatgcgt +f8v4s72 f8v0s68 0.015873 F 507278 aagacttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f3v1s32 f3v3s34 0.025641 F 838714 cgaaaaagagtgaatgaaatggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v4s35 f3v3s34 0.051948 F 838714 cgaaaaaggtgaagaaaggctgggacgctcctggttggcgcgctctaggtgttgtggcttgaacgttagcaccta +f11v6s97 f11v8s99 0.068493 F 421503 agcgttagcaaagaacctggttaatcgccctcgaggatcggtggtggatcggagatgcgggcatcgcgtttc +f4v1s45 f4v8s52 0.089552 F 359938 ctgtatttcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactatagagaaga +f0v5s5 f1v5s14 0.075000 F 234004 ggatcacagtctacactgctcactcccccccggcccctgagtccgaggagagggtgcttcaaagtatgtataccactg +f5v5s58 f5v3s56 0.090909 F 192648 cgcaacgcaccattaagatggtaatcactaaaaaaatcggcctaacctgtccaatcgccactt +f1v0s9 f1v4s13 0.024691 F 304149 gaatcagcttctaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactcgacgattc +f8v6s74 f8v0s68 0.030769 F 507278 aagacccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgccgt +f6v0s60 f6v0s60 0.000000 T 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctacg +f10v8s85 f10v7s84 0.080000 F 905994 ttgccggacactcgaatcttccaagcagaggtccagctaacacacagcaggcatactagtcccgatgggtagc +f10v1s78 f10v7s84 0.066667 F 905994 ttgccggacactcgcaatcctccaagcagaggtccagctaacacacagcaggcatttagtcccgatgaggtagc +f5v4s57 f5v3s56 0.075758 F 192648 cggcaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactat +f3v10s41 f3v3s34 0.051282 F 838714 cgaaaataaggtgaatgaaagggtgggacgctcctagtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v7s38 f3v3s34 0.025974 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttcgtgcttgaacgttagcacct +f10v6s83 f10v7s84 0.067568 F 905994 ttccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcctgatgggtagc +f11v2s93 f11v8s99 0.041667 F 421503 agcgttagcaaagagcctggttaatcgccctcggaggatcggtggtgatcagagatgcgggcatccgttc +f3v9s40 f3v3s34 0.013158 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggggttgtggcttgaacgttagcacct +f11v4s95 f11v8s99 0.055556 F 421503 agcgttagcaaagagcctggttaatcccctcgagatcgggtggtgatcagagatgcgggcatccgtttc +f8v1s69 f8v0s68 0.015625 F 507278 aagaccttgaacacctctaagcgaaaattggatatcgagtcagggaacataaccacgggtgcgt +f11v5s96 f11v8s99 0.055556 F 421503 agcgttagcaaagagcctggttaatggccctcgaggatcggtggctgaccagagatgcgggcatccgtttc +f6v2s62 f6v0s60 0.031746 F 100983 catgtcgagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctac +f4v6s50 f4v8s52 0.089552 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagacgaagag +f9v0s76 f9v0s76 0.000000 T 29993 gggatcgtggcaagcaacccttgccgcctcgactgcggctaaagacgcatgttccagtaataccta +f2v12s30 f2v8s26 0.044776 F 863655 tggactagtagaccgtgtgccacaatcgacgctgaggacacgttgatttttatacggcgtctcag +f11v9s100 f11v8s99 0.027778 F 421503 agcgttagcaaaggagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f4v5s49 f4v8s52 0.075758 F 359938 ctatattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaagca +f2v9s27 f2v8s26 0.014925 F 863655 tggactagtagaccgtgtgcctaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f7v3s67 f7v0s64 0.016393 F 114016 gcttcgtgggaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f0v8s8 f0v7s7 0.094118 F 281628 ggatcacagtcctacactgctcactccaaccccggccctgagtccgaggagagggtgcttcagagtatgtataccactg +f2v6s24 f2v8s26 0.059701 F 863655 tggactagtagacgtgtgcctcatcgacgcgtgagacactttgatttcttatacggcgtctcag +f11v3s94 f11v8s99 0.041667 F 421503 agcgttagcaaagagcctggttaatcgccgtcgaaggatcggtggtgatcagagatgcgggcatccgtttc +f6v3s63 f6v0s60 0.046154 F 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggttttgggtcacgtctccatggctacg +f3v11s42 f3v3s34 0.026316 F 838714 cgaaaaaggtgaatgaaagtctgggacgctcctggtttgggacgctctaggtgttgtggcttgaacgttagcacct +f4v4s48 f4v8s52 0.076923 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaa +f3v8s39 f3v3s34 0.051948 F 838714 cgaaaaaggtgaatgaaaggcctgggacgctcctggttggggcgctctaggtgtgtggcttgaacgttagacct +f7v1s65 f7v0s64 0.032258 F 114016 gcttcggggaaccagagacaaccgacatatttcgaaccaccgacatacgatcctttaaacc +f5v1s54 f5v3s56 0.076923 F 192648 cggaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactt +f4v7s51 f4v7s51 0.000000 T 8599 cgtttattcgtaggatctactacgcatcaaagtatcccacttttgtaccccactaagagcaga +f7v2s66 f7v0s64 0.048387 F 114016 gcttcgtgggaaccagaacaaccgacatattccgaaccaccggacatacgatccttaaacc +f1v6s15 f1v4s13 0.049383 F 304149 gaatcagcttctaaccaagcgttgcagaaccggctactttaagcatgatgaatgcgtcgtaagtgatactcgacgattc +f5v2s55 f5v3s56 0.092308 F 192648 cggcatcgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccact diff --git a/pkg/obitools/obiclust/testdata/families_exact.expected b/pkg/obitools/obiclust/testdata/families_exact.expected new file mode 100644 index 0000000..e8c2b06 --- /dev/null +++ b/pkg/obitools/obiclust/testdata/families_exact.expected @@ -0,0 +1,101 @@ +f2v8s26 f2v8s26 1.000000 T 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v3s80 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v0s18 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v7s25 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v9s86 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v3s21 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f6v1s61 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f2v2s20 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f3v3s34 f3v3s34 1.000000 T 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f4v0s44 f3v3s34 1.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v0s31 f3v3s34 1.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f0v7s7 f0v7s7 1.000000 T 173457 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v2s11 f0v7s7 1.000000 F 173457 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v1s10 f0v7s7 1.000000 F 173457 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f3v6s37 f3v3s34 0.935897 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f5v0s53 f3v3s34 0.935897 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f11v7s98 f3v3s34 0.935897 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f2v4s22 f2v8s26 0.970588 F 863655 tggactagtagaccgtgtcgcctcaatcgacgcgtgaggacacgttgattcttatacggcgtctcag +f10v7s84 f10v7s84 1.000000 T 905994 gtgccggacactcgcaatcttcacaagcagaggtccagctaacacacagcagcatttagtcccgatgggtagc +f10v0s77 f10v7s84 0.959459 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f5v6s59 f2v8s26 0.985075 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v10s28 f2v8s26 0.985075 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f1v5s14 f1v5s14 1.000000 T 342175 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v4s4 f1v5s14 1.000000 F 342175 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v1s1 f1v5s14 1.000000 F 342175 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v3s3 f1v5s14 1.000000 F 342175 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f5v3s56 f5v3s56 1.000000 T 192648 ccgcaacgcaccataagatgtaatcactaagaaaaatcggcctaacctgtcatatcgccactt +f10v2s79 f10v7s84 0.920000 F 905994 ttgccggtcactcgcaatcttaccaaccagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v13s90 f10v7s84 0.907895 F 905994 tgccggacactcgcaatctccaagcagagagtccaagctaacacacagcaggcatttgtcccgatgggtagc +f1v4s13 f1v4s13 1.000000 T 304149 gaatcagcttctcaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactggacgattc +f10v4s81 f10v7s84 0.933333 F 905994 ttgccggacactggcaatcttccatagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v5s82 f10v7s84 0.945946 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacatagcaggcatttagtcccgatgggtagc +f4v8s52 f4v8s52 1.000000 T 359938 ctgatattcgtattgatctactacgtatcaaagtatcccacttttgtgaccccactaagagaaga +f2v11s29 f2v8s26 0.955882 F 863655 tggactgtagaaccgtgtgcctcaatccacgcgtgaggacacgttgatttcttatacggcgtctcag +f0v6s6 f1v5s14 0.925000 F 342175 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f1v3s12 f1v5s14 0.925000 F 342175 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f0v0s0 f1v5s14 0.962500 F 342175 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f0v2s2 f1v5s14 0.962500 F 342175 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f1v7s16 f1v4s13 0.913580 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f2v5s23 f1v4s13 0.913580 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f3v12s43 f3v3s34 0.948718 F 838714 cgaaaaaggtgaatgaaagactgggacgcctcctggtttgggacgctctaggtggttgtggcttgaacgttagcacct +f8v0s68 f8v0s68 1.000000 T 507278 aagaccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f2v1s19 f1v4s13 0.902439 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f1v8s17 f1v4s13 0.902439 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f10v11s88 f10v7s84 0.933333 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaactcacagccaggcatttagtcccgatgggtagc +f3v5s36 f3v3s34 0.973684 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttatggcttgaaagttagcacct +f8v7s75 f8v0s68 0.968750 F 507278 aagaccttgaacacctctaagcgaaaattggattccagtcagggaaccataaccacgggtgcgt +f10v12s89 f10v7s84 0.933333 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacacgcaggcatttagacccgatgggtagc +f4v2s46 f4v8s52 0.938462 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaga +f8v2s70 f8v0s68 0.925373 F 507278 aagaccttgaacaacctctaagccgaaattggaattcgagtcagggaacataaccacgggtgcgat +f11v8s99 f11v8s99 1.000000 T 421503 agcgttagcaaagagcctggttaatctgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f7v0s64 f7v0s64 1.000000 T 114016 gcttcgtgggaaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f11v1s92 f11v8s99 0.944444 F 421503 agcgttagcaaagagcctggttcatcgccctcgaggatcggtggtgatcaagagatgcggcatccgtttc +f3v2s33 f3v3s34 0.948052 F 838714 cgaaaaaaggtaatgaaaggctgggatgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcact +f8v3s71 f8v0s68 0.952381 F 507278 aagaccttgaacacctctaagcgaaattggattcgagtcagcgaacataacacgggtgcgt +f4v3s47 f4v8s52 0.923077 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaaagaaga +f10v10s87 f10v7s84 0.906667 F 905994 tctgccggacactcgcaatcttccaagcagaggtccagctaacacacgcaggcatttatccgatgggtagc +f11v0s91 f11v8s99 0.985915 F 421503 agcgttagcaaagagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f8v5s73 f8v0s68 0.953125 F 507278 aagaccttgtacacctctaagcgataaattggattcgagtcagggaacataaccacggatgcgt +f8v4s72 f8v0s68 0.984127 F 507278 aagacttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f3v1s32 f3v3s34 0.974359 F 838714 cgaaaaagagtgaatgaaatggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v4s35 f3v3s34 0.948052 F 838714 cgaaaaaggtgaagaaaggctgggacgctcctggttggcgcgctctaggtgttgtggcttgaacgttagcaccta +f11v6s97 f11v8s99 0.931507 F 421503 agcgttagcaaagaacctggttaatcgccctcgaggatcggtggtggatcggagatgcgggcatcgcgtttc +f4v1s45 f4v8s52 0.910448 F 359938 ctgtatttcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactatagagaaga +f0v5s5 f1v5s14 0.925000 F 342175 ggatcacagtctacactgctcactcccccccggcccctgagtccgaggagagggtgcttcaaagtatgtataccactg +f5v5s58 f5v3s56 0.909091 F 192648 cgcaacgcaccattaagatggtaatcactaaaaaaatcggcctaacctgtccaatcgccactt +f1v0s9 f1v4s13 0.975309 F 304149 gaatcagcttctaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactcgacgattc +f8v6s74 f8v0s68 0.969231 F 507278 aagacccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgccgt +f6v0s60 f6v0s60 1.000000 T 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctacg +f10v8s85 f10v7s84 0.920000 F 905994 ttgccggacactcgaatcttccaagcagaggtccagctaacacacagcaggcatactagtcccgatgggtagc +f10v1s78 f10v7s84 0.933333 F 905994 ttgccggacactcgcaatcctccaagcagaggtccagctaacacacagcaggcatttagtcccgatgaggtagc +f5v4s57 f5v3s56 0.924242 F 192648 cggcaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactat +f3v10s41 f3v3s34 0.948718 F 838714 cgaaaataaggtgaatgaaagggtgggacgctcctagtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v7s38 f3v3s34 0.974026 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttcgtgcttgaacgttagcacct +f10v6s83 f10v7s84 0.932432 F 905994 ttccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcctgatgggtagc +f11v2s93 f11v8s99 0.958333 F 421503 agcgttagcaaagagcctggttaatcgccctcggaggatcggtggtgatcagagatgcgggcatccgttc +f3v9s40 f3v3s34 0.986842 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggggttgtggcttgaacgttagcacct +f11v4s95 f11v8s99 0.944444 F 421503 agcgttagcaaagagcctggttaatcccctcgagatcgggtggtgatcagagatgcgggcatccgtttc +f8v1s69 f8v0s68 0.984375 F 507278 aagaccttgaacacctctaagcgaaaattggatatcgagtcagggaacataaccacgggtgcgt +f11v5s96 f11v8s99 0.944444 F 421503 agcgttagcaaagagcctggttaatggccctcgaggatcggtggctgaccagagatgcgggcatccgtttc +f6v2s62 f6v0s60 0.968254 F 100983 catgtcgagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctac +f4v6s50 f4v8s52 0.910448 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagacgaagag +f9v0s76 f9v0s76 1.000000 T 29993 gggatcgtggcaagcaacccttgccgcctcgactgcggctaaagacgcatgttccagtaataccta +f2v12s30 f2v8s26 0.955224 F 863655 tggactagtagaccgtgtgccacaatcgacgctgaggacacgttgatttttatacggcgtctcag +f11v9s100 f11v8s99 0.972222 F 421503 agcgttagcaaaggagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f4v5s49 f4v8s52 0.924242 F 359938 ctatattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaagca +f2v9s27 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f7v3s67 f7v0s64 0.983607 F 114016 gcttcgtgggaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f0v8s8 f1v5s14 0.950000 F 342175 ggatcacagtcctacactgctcactccaaccccggccctgagtccgaggagagggtgcttcagagtatgtataccactg +f2v6s24 f2v8s26 0.940299 F 863655 tggactagtagacgtgtgcctcatcgacgcgtgagacactttgatttcttatacggcgtctcag +f11v3s94 f11v8s99 0.958333 F 421503 agcgttagcaaagagcctggttaatcgccgtcgaaggatcggtggtgatcagagatgcgggcatccgtttc +f6v3s63 f6v0s60 0.953846 F 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggttttgggtcacgtctccatggctacg +f3v11s42 f3v3s34 0.973684 F 838714 cgaaaaaggtgaatgaaagtctgggacgctcctggtttgggacgctctaggtgttgtggcttgaacgttagcacct +f4v4s48 f4v8s52 0.923077 F 359938 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaa +f3v8s39 f3v3s34 0.948052 F 838714 cgaaaaaggtgaatgaaaggcctgggacgctcctggttggggcgctctaggtgtgtggcttgaacgttagacct +f7v1s65 f7v0s64 0.967742 F 114016 gcttcggggaaccagagacaaccgacatatttcgaaccaccgacatacgatcctttaaacc +f5v1s54 f5v3s56 0.923077 F 192648 cggaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactt +f4v7s51 f4v7s51 1.000000 T 8599 cgtttattcgtaggatctactacgcatcaaagtatcccacttttgtaccccactaagagcaga +f7v2s66 f7v0s64 0.951613 F 114016 gcttcgtgggaaccagaacaaccgacatattccgaaccaccggacatacgatccttaaacc +f1v6s15 f1v4s13 0.950617 F 304149 gaatcagcttctaaccaagcgttgcagaaccggctactttaagcatgatgaatgcgtcgtaagtgatactcgacgattc +f5v2s55 f5v3s56 0.907692 F 192648 cggcatcgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccact diff --git a/pkg/obitools/obiclust/testdata/families_longest.expected b/pkg/obitools/obiclust/testdata/families_longest.expected new file mode 100644 index 0000000..f209e8e --- /dev/null +++ b/pkg/obitools/obiclust/testdata/families_longest.expected @@ -0,0 +1,101 @@ +f2v8s26 f2v8s26 1.000000 T 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v3s80 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v0s18 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v7s25 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v9s86 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v3s21 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f6v1s61 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f2v2s20 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f3v3s34 f3v3s34 1.000000 T 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f4v0s44 f3v3s34 1.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v0s31 f3v3s34 1.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f0v7s7 f0v7s7 1.000000 T 515632 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v2s11 f0v7s7 1.000000 F 515632 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v1s10 f0v7s7 1.000000 F 515632 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f3v6s37 f3v3s34 0.960526 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f5v0s53 f3v3s34 0.960526 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f11v7s98 f3v3s34 0.960526 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f2v4s22 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtcgcctcaatcgacgcgtgaggacacgttgattcttatacggcgtctcag +f10v7s84 f10v7s84 1.000000 T 905994 gtgccggacactcgcaatcttcacaagcagaggtccagctaacacacagcagcatttagtcccgatgggtagc +f10v0s77 f10v7s84 0.972603 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f5v6s59 f2v8s26 0.985075 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v10s28 f2v8s26 0.985075 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f1v5s14 f0v7s7 0.892857 F 515632 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v4s4 f0v7s7 0.892857 F 515632 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v1s1 f0v7s7 0.892857 F 515632 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v3s3 f0v7s7 0.892857 F 515632 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f5v3s56 f5v3s56 1.000000 T 192648 ccgcaacgcaccataagatgtaatcactaagaaaaatcggcctaacctgtcatatcgccactt +f10v2s79 f10v7s84 0.932432 F 905994 ttgccggtcactcgcaatcttaccaaccagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v13s90 f10v7s84 0.945205 F 905994 tgccggacactcgcaatctccaagcagagagtccaagctaacacacagcaggcatttgtcccgatgggtagc +f1v4s13 f1v4s13 1.000000 T 304149 gaatcagcttctcaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactggacgattc +f10v4s81 f10v7s84 0.945946 F 905994 ttgccggacactggcaatcttccatagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v5s82 f10v7s84 0.958904 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacatagcaggcatttagtcccgatgggtagc +f4v8s52 f4v8s52 1.000000 T 368537 ctgatattcgtattgatctactacgtatcaaagtatcccacttttgtgaccccactaagagaaga +f2v11s29 f2v8s26 0.970149 F 863655 tggactgtagaaccgtgtgcctcaatccacgcgtgaggacacgttgatttcttatacggcgtctcag +f0v6s6 f0v7s7 0.880952 F 515632 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f1v3s12 f0v7s7 0.880952 F 515632 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f0v0s0 f0v7s7 0.916667 F 515632 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f0v2s2 f0v7s7 0.916667 F 515632 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f1v7s16 f1v4s13 0.913580 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f2v5s23 f1v4s13 0.913580 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f3v12s43 f3v3s34 0.948718 F 838714 cgaaaaaggtgaatgaaagactgggacgcctcctggtttgggacgctctaggtggttgtggcttgaacgttagcacct +f8v0s68 f8v0s68 1.000000 T 507278 aagaccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f2v1s19 f1v4s13 0.913580 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f1v8s17 f1v4s13 0.913580 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f10v11s88 f10v7s84 0.945946 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaactcacagccaggcatttagtcccgatgggtagc +f3v5s36 f3v3s34 0.973684 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttatggcttgaaagttagcacct +f8v7s75 f8v0s68 0.968750 F 507278 aagaccttgaacacctctaagcgaaaattggattccagtcagggaaccataaccacgggtgcgt +f10v12s89 f10v7s84 0.945946 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacacgcaggcatttagacccgatgggtagc +f4v2s46 f4v8s52 0.938462 F 368537 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaga +f8v2s70 f8v0s68 0.939394 F 507278 aagaccttgaacaacctctaagccgaaattggaattcgagtcagggaacataaccacgggtgcgat +f11v8s99 f11v8s99 1.000000 T 421503 agcgttagcaaagagcctggttaatctgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f7v0s64 f7v0s64 1.000000 T 114016 gcttcgtgggaaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f11v1s92 f11v8s99 0.957746 F 421503 agcgttagcaaagagcctggttcatcgccctcgaggatcggtggtgatcaagagatgcggcatccgtttc +f3v2s33 f3v3s34 0.960526 F 838714 cgaaaaaaggtaatgaaaggctgggatgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcact +f8v3s71 f8v0s68 0.952381 F 507278 aagaccttgaacacctctaagcgaaattggattcgagtcagcgaacataacacgggtgcgt +f4v3s47 f4v8s52 0.923077 F 368537 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaaagaaga +f10v10s87 f10v7s84 0.931507 F 905994 tctgccggacactcgcaatcttccaagcagaggtccagctaacacacgcaggcatttatccgatgggtagc +f11v0s91 f11v8s99 0.985915 F 421503 agcgttagcaaagagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f8v5s73 f8v0s68 0.953125 F 507278 aagaccttgtacacctctaagcgataaattggattcgagtcagggaacataaccacggatgcgt +f8v4s72 f8v0s68 0.984127 F 507278 aagacttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f3v1s32 f3v3s34 0.974359 F 838714 cgaaaaagagtgaatgaaatggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v4s35 f3v3s34 0.960526 F 838714 cgaaaaaggtgaagaaaggctgggacgctcctggttggcgcgctctaggtgttgtggcttgaacgttagcaccta +f11v6s97 f11v8s99 0.944444 F 421503 agcgttagcaaagaacctggttaatcgccctcgaggatcggtggtggatcggagatgcgggcatcgcgtttc +f4v1s45 f4v8s52 0.938462 F 368537 ctgtatttcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactatagagaaga +f0v5s5 f0v7s7 0.880952 F 515632 ggatcacagtctacactgctcactcccccccggcccctgagtccgaggagagggtgcttcaaagtatgtataccactg +f5v5s58 f5v3s56 0.952381 F 192648 cgcaacgcaccattaagatggtaatcactaaaaaaatcggcctaacctgtccaatcgccactt +f1v0s9 f1v4s13 0.975309 F 304149 gaatcagcttctaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactcgacgattc +f8v6s74 f8v0s68 0.969231 F 507278 aagacccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgccgt +f6v0s60 f6v0s60 1.000000 T 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctacg +f10v8s85 f10v7s84 0.945205 F 905994 ttgccggacactcgaatcttccaagcagaggtccagctaacacacagcaggcatactagtcccgatgggtagc +f10v1s78 f10v7s84 0.945946 F 905994 ttgccggacactcgcaatcctccaagcagaggtccagctaacacacagcaggcatttagtcccgatgaggtagc +f5v4s57 f5v3s56 0.938462 F 192648 cggcaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactat +f3v10s41 f3v3s34 0.948718 F 838714 cgaaaataaggtgaatgaaagggtgggacgctcctagtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v7s38 f3v3s34 0.986842 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttcgtgcttgaacgttagcacct +f10v6s83 f10v7s84 0.945205 F 905994 ttccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcctgatgggtagc +f11v2s93 f11v8s99 0.971831 F 421503 agcgttagcaaagagcctggttaatcgccctcggaggatcggtggtgatcagagatgcgggcatccgttc +f3v9s40 f3v3s34 0.986842 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggggttgtggcttgaacgttagcacct +f11v4s95 f11v8s99 0.957746 F 421503 agcgttagcaaagagcctggttaatcccctcgagatcgggtggtgatcagagatgcgggcatccgtttc +f8v1s69 f8v0s68 0.984375 F 507278 aagaccttgaacacctctaagcgaaaattggatatcgagtcagggaacataaccacgggtgcgt +f11v5s96 f11v8s99 0.957746 F 421503 agcgttagcaaagagcctggttaatggccctcgaggatcggtggctgaccagagatgcgggcatccgtttc +f6v2s62 f6v0s60 0.968254 F 100983 catgtcgagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctac +f4v6s50 f4v8s52 0.938462 F 368537 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagacgaagag +f9v0s76 f9v0s76 1.000000 T 29993 gggatcgtggcaagcaacccttgccgcctcgactgcggctaaagacgcatgttccagtaataccta +f2v12s30 f2v8s26 0.955224 F 863655 tggactagtagaccgtgtgccacaatcgacgctgaggacacgttgatttttatacggcgtctcag +f11v9s100 f11v8s99 0.985915 F 421503 agcgttagcaaaggagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f4v5s49 f4v8s52 0.938462 F 368537 ctatattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaagca +f2v9s27 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtgcctaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f7v3s67 f7v0s64 0.983607 F 114016 gcttcgtgggaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f0v8s8 f0v7s7 0.916667 F 515632 ggatcacagtcctacactgctcactccaaccccggccctgagtccgaggagagggtgcttcagagtatgtataccactg +f2v6s24 f2v8s26 0.940299 F 863655 tggactagtagacgtgtgcctcatcgacgcgtgagacactttgatttcttatacggcgtctcag +f11v3s94 f11v8s99 0.971831 F 421503 agcgttagcaaagagcctggttaatcgccgtcgaaggatcggtggtgatcagagatgcgggcatccgtttc +f6v3s63 f6v0s60 0.953846 F 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggttttgggtcacgtctccatggctacg +f3v11s42 f3v3s34 0.973684 F 838714 cgaaaaaggtgaatgaaagtctgggacgctcctggtttgggacgctctaggtgttgtggcttgaacgttagcacct +f4v4s48 f4v8s52 0.923077 F 368537 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaa +f3v8s39 f3v3s34 0.960526 F 838714 cgaaaaaggtgaatgaaaggcctgggacgctcctggttggggcgctctaggtgtgtggcttgaacgttagacct +f7v1s65 f7v0s64 0.983607 F 114016 gcttcggggaaccagagacaaccgacatatttcgaaccaccgacatacgatcctttaaacc +f5v1s54 f5v3s56 0.952381 F 192648 cggaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactt +f4v7s51 f4v8s52 0.892308 F 368537 cgtttattcgtaggatctactacgcatcaaagtatcccacttttgtaccccactaagagcaga +f7v2s66 f7v0s64 0.967213 F 114016 gcttcgtgggaaccagaacaaccgacatattccgaaccaccggacatacgatccttaaacc +f1v6s15 f1v4s13 0.950617 F 304149 gaatcagcttctaaccaagcgttgcagaaccggctactttaagcatgatgaatgcgtcgtaagtgatactcgacgattc +f5v2s55 f5v3s56 0.936508 F 192648 cggcatcgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccact diff --git a/pkg/obitools/obiclust/testdata/families_ratio.expected b/pkg/obitools/obiclust/testdata/families_ratio.expected new file mode 100644 index 0000000..a129dde --- /dev/null +++ b/pkg/obitools/obiclust/testdata/families_ratio.expected @@ -0,0 +1,101 @@ +f2v8s26 f2v8s26 1.000000 T 619902 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v3s80 f2v8s26 1.000000 F 619902 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v0s18 f2v8s26 1.000000 F 619902 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v7s25 f2v8s26 1.000000 F 619902 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v9s86 f2v8s26 1.000000 F 619902 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v3s21 f2v3s21 1.000000 T 243753 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f6v1s61 f2v3s21 1.000000 F 243753 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f2v2s20 f2v3s21 1.000000 F 243753 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f3v3s34 f3v3s34 1.000000 T 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f4v0s44 f3v3s34 1.000000 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v0s31 f3v3s34 1.000000 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f0v7s7 f0v7s7 1.000000 T 281628 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v2s11 f0v7s7 1.000000 F 281628 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v1s10 f0v7s7 1.000000 F 281628 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f3v6s37 f3v6s37 1.000000 T 167833 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f5v0s53 f3v6s37 1.000000 F 167833 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f11v7s98 f3v6s37 1.000000 F 167833 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f2v4s22 f2v8s26 0.970588 F 619902 tggactagtagaccgtgtcgcctcaatcgacgcgtgaggacacgttgattcttatacggcgtctcag +f10v7s84 f10v7s84 1.000000 T 224921 gtgccggacactcgcaatcttcacaagcagaggtccagctaacacacagcagcatttagtcccgatgggtagc +f10v0s77 f10v0s77 1.000000 T 97674 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f5v6s59 f2v8s26 0.985075 F 619902 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v10s28 f2v8s26 0.985075 F 619902 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f1v5s14 f1v5s14 1.000000 T 95466 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v4s4 f1v5s14 1.000000 F 95466 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v1s1 f1v5s14 1.000000 F 95466 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v3s3 f1v5s14 1.000000 F 95466 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f5v3s56 f5v3s56 1.000000 T 142796 ccgcaacgcaccataagatgtaatcactaagaaaaatcggcctaacctgtcatatcgccactt +f10v2s79 f10v2s79 1.000000 T 93964 ttgccggtcactcgcaatcttaccaaccagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v13s90 f10v13s90 1.000000 T 92493 tgccggacactcgcaatctccaagcagagagtccaagctaacacacagcaggcatttgtcccgatgggtagc +f1v4s13 f1v4s13 1.000000 T 93209 gaatcagcttctcaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactggacgattc +f10v4s81 f10v4s81 1.000000 T 88973 ttgccggacactggcaatcttccatagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v5s82 f10v5s82 1.000000 T 87843 ttgccggacactcgcaatcttccaagcagaggtccagctaacacatagcaggcatttagtcccgatgggtagc +f4v8s52 f4v8s52 1.000000 T 160201 ctgatattcgtattgatctactacgtatcaaagtatcccacttttgtgaccccactaagagaaga +f2v11s29 f2v8s26 0.955882 F 619902 tggactgtagaaccgtgtgcctcaatccacgcgtgaggacacgttgatttcttatacggcgtctcag +f0v6s6 f0v6s6 1.000000 T 84418 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f1v3s12 f0v6s6 1.000000 F 84418 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f0v0s0 f0v7s7 0.916667 F 281628 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f0v2s2 f0v7s7 0.916667 F 281628 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f1v7s16 f1v7s16 1.000000 T 81657 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f2v5s23 f1v7s16 1.000000 F 81657 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f3v12s43 f3v3s34 0.948718 F 670881 cgaaaaaggtgaatgaaagactgggacgcctcctggtttgggacgctctaggtggttgtggcttgaacgttagcacct +f8v0s68 f8v0s68 1.000000 T 114020 aagaccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f2v1s19 f2v1s19 1.000000 T 79492 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f1v8s17 f2v1s19 1.000000 F 79492 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f10v11s88 f10v11s88 1.000000 T 78894 ttgccggacactcgcaatcttccaagcagaggtccagctaactcacagccaggcatttagtcccgatgggtagc +f3v5s36 f3v3s34 0.973684 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttatggcttgaaagttagcacct +f8v7s75 f8v7s75 1.000000 T 75803 aagaccttgaacacctctaagcgaaaattggattccagtcagggaaccataaccacgggtgcgt +f10v12s89 f10v12s89 1.000000 T 74438 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacacgcaggcatttagacccgatgggtagc +f4v2s46 f4v2s46 1.000000 T 83032 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaga +f8v2s70 f8v2s70 1.000000 T 74088 aagaccttgaacaacctctaagccgaaattggaattcgagtcagggaacataaccacgggtgcgat +f11v8s99 f11v8s99 1.000000 T 224582 agcgttagcaaagagcctggttaatctgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f7v0s64 f7v0s64 1.000000 T 114016 gcttcgtgggaaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f11v1s92 f11v1s92 1.000000 T 71853 agcgttagcaaagagcctggttcatcgccctcgaggatcggtggtgatcaagagatgcggcatccgtttc +f3v2s33 f3v3s34 0.948052 F 670881 cgaaaaaaggtaatgaaaggctgggatgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcact +f8v3s71 f8v3s71 1.000000 T 70497 aagaccttgaacacctctaagcgaaattggattcgagtcagcgaacataacacgggtgcgt +f4v3s47 f4v3s47 1.000000 T 69396 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaaagaaga +f10v10s87 f10v10s87 1.000000 T 66794 tctgccggacactcgcaatcttccaagcagaggtccagctaacacacgcaggcatttatccgatgggtagc +f11v0s91 f11v0s91 1.000000 T 66160 agcgttagcaaagagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f8v5s73 f8v5s73 1.000000 T 64084 aagaccttgtacacctctaagcgataaattggattcgagtcagggaacataaccacggatgcgt +f8v4s72 f8v4s72 1.000000 T 59724 aagacttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f3v1s32 f3v3s34 0.974359 F 670881 cgaaaaagagtgaatgaaatggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v4s35 f3v3s34 0.948052 F 670881 cgaaaaaggtgaagaaaggctgggacgctcctggttggcgcgctctaggtgttgtggcttgaacgttagcaccta +f11v6s97 f11v6s97 1.000000 T 58908 agcgttagcaaagaacctggttaatcgccctcgaggatcggtggtggatcggagatgcgggcatcgcgtttc +f4v1s45 f4v1s45 1.000000 T 55908 ctgtatttcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactatagagaaga +f0v5s5 f0v5s5 1.000000 T 54120 ggatcacagtctacactgctcactcccccccggcccctgagtccgaggagagggtgcttcaaagtatgtataccactg +f5v5s58 f5v5s58 1.000000 T 49852 cgcaacgcaccattaagatggtaatcactaaaaaaatcggcctaacctgtccaatcgccactt +f1v0s9 f1v0s9 1.000000 T 49791 gaatcagcttctaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactcgacgattc +f8v6s74 f8v6s74 1.000000 T 49062 aagacccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgccgt +f6v0s60 f6v0s60 1.000000 T 67982 catgtcaagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctacg +f10v8s85 f10v7s84 0.920000 F 224921 ttgccggacactcgaatcttccaagcagaggtccagctaacacacagcaggcatactagtcccgatgggtagc +f10v1s78 f10v7s84 0.933333 F 224921 ttgccggacactcgcaatcctccaagcagaggtccagctaacacacagcaggcatttagtcccgatgaggtagc +f5v4s57 f5v3s56 0.924242 F 142796 cggcaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactat +f3v10s41 f3v3s34 0.948718 F 670881 cgaaaataaggtgaatgaaagggtgggacgctcctagtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v7s38 f3v3s34 0.974026 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttcgtgcttgaacgttagcacct +f10v6s83 f10v7s84 0.932432 F 224921 ttccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcctgatgggtagc +f11v2s93 f11v8s99 0.958333 F 224582 agcgttagcaaagagcctggttaatcgccctcggaggatcggtggtgatcagagatgcgggcatccgttc +f3v9s40 f3v3s34 0.986842 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggggttgtggcttgaacgttagcacct +f11v4s95 f11v8s99 0.944444 F 224582 agcgttagcaaagagcctggttaatcccctcgagatcgggtggtgatcagagatgcgggcatccgtttc +f8v1s69 f8v0s68 0.984375 F 114020 aagaccttgaacacctctaagcgaaaattggatatcgagtcagggaacataaccacgggtgcgt +f11v5s96 f11v8s99 0.944444 F 224582 agcgttagcaaagagcctggttaatggccctcgaggatcggtggctgaccagagatgcgggcatccgtttc +f6v2s62 f6v2s62 1.000000 T 33001 catgtcgagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctac +f4v6s50 f4v8s52 0.910448 F 160201 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagacgaagag +f9v0s76 f9v0s76 1.000000 T 29993 gggatcgtggcaagcaacccttgccgcctcgactgcggctaaagacgcatgttccagtaataccta +f2v12s30 f2v8s26 0.955224 F 619902 tggactagtagaccgtgtgccacaatcgacgctgaggacacgttgatttttatacggcgtctcag +f11v9s100 f11v8s99 0.972222 F 224582 agcgttagcaaaggagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f4v5s49 f4v8s52 0.924242 F 160201 ctatattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaagca +f2v9s27 f2v8s26 0.985075 F 619902 tggactagtagaccgtgtgcctaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f7v3s67 f7v0s64 0.983607 F 114016 gcttcgtgggaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f0v8s8 f0v7s7 0.905882 F 281628 ggatcacagtcctacactgctcactccaaccccggccctgagtccgaggagagggtgcttcagagtatgtataccactg +f2v6s24 f2v8s26 0.940299 F 619902 tggactagtagacgtgtgcctcatcgacgcgtgagacactttgatttcttatacggcgtctcag +f11v3s94 f11v8s99 0.958333 F 224582 agcgttagcaaagagcctggttaatcgccgtcgaaggatcggtggtgatcagagatgcgggcatccgtttc +f6v3s63 f6v0s60 0.953846 F 67982 catgtcaagagactggaaccccggtgatgtgaaaaaaggttttgggtcacgtctccatggctacg +f3v11s42 f3v3s34 0.973684 F 670881 cgaaaaaggtgaatgaaagtctgggacgctcctggtttgggacgctctaggtgttgtggcttgaacgttagcacct +f4v4s48 f4v8s52 0.923077 F 160201 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaa +f3v8s39 f3v3s34 0.948052 F 670881 cgaaaaaggtgaatgaaaggcctgggacgctcctggttggggcgctctaggtgtgtggcttgaacgttagacct +f7v1s65 f7v0s64 0.967742 F 114016 gcttcggggaaccagagacaaccgacatatttcgaaccaccgacatacgatcctttaaacc +f5v1s54 f5v3s56 0.923077 F 142796 cggaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactt +f4v7s51 f4v2s46 0.937500 F 83032 cgtttattcgtaggatctactacgcatcaaagtatcccacttttgtaccccactaagagcaga +f7v2s66 f7v0s64 0.951613 F 114016 gcttcgtgggaaccagaacaaccgacatattccgaaccaccggacatacgatccttaaacc +f1v6s15 f1v4s13 0.950617 F 93209 gaatcagcttctaaccaagcgttgcagaaccggctactttaagcatgatgaatgcgtcgtaagtgatactcgacgattc +f5v2s55 f5v3s56 0.907692 F 142796 cggcatcgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccact diff --git a/pkg/obitools/obiclust/testdata/families_rawdist.expected b/pkg/obitools/obiclust/testdata/families_rawdist.expected new file mode 100644 index 0000000..243b7d2 --- /dev/null +++ b/pkg/obitools/obiclust/testdata/families_rawdist.expected @@ -0,0 +1,101 @@ +f2v8s26 f2v8s26 0.000000 T 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v3s80 f2v8s26 0.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v0s18 f2v8s26 0.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v7s25 f2v8s26 0.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v9s86 f2v8s26 0.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v3s21 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f6v1s61 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f2v2s20 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f3v3s34 f3v3s34 0.000000 T 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f4v0s44 f3v3s34 0.000000 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v0s31 f3v3s34 0.000000 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f0v7s7 f0v7s7 0.000000 T 173457 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v2s11 f0v7s7 0.000000 F 173457 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v1s10 f0v7s7 0.000000 F 173457 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f3v6s37 f3v6s37 0.000000 T 167833 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f5v0s53 f3v6s37 0.000000 F 167833 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f11v7s98 f3v6s37 0.000000 F 167833 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f2v4s22 f2v8s26 2.000000 F 863655 tggactagtagaccgtgtcgcctcaatcgacgcgtgaggacacgttgattcttatacggcgtctcag +f10v7s84 f10v7s84 0.000000 T 196352 gtgccggacactcgcaatcttcacaagcagaggtccagctaacacacagcagcatttagtcccgatgggtagc +f10v0s77 f10v7s84 3.000000 F 196352 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f5v6s59 f2v8s26 1.000000 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v10s28 f2v8s26 1.000000 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f1v5s14 f1v5s14 0.000000 T 203637 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v4s4 f1v5s14 0.000000 F 203637 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v1s1 f1v5s14 0.000000 F 203637 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v3s3 f1v5s14 0.000000 F 203637 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f5v3s56 f5v3s56 0.000000 T 94426 ccgcaacgcaccataagatgtaatcactaagaaaaatcggcctaacctgtcatatcgccactt +f10v2s79 f10v2s79 0.000000 T 93964 ttgccggtcactcgcaatcttaccaaccagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v13s90 f10v13s90 0.000000 T 92493 tgccggacactcgcaatctccaagcagagagtccaagctaacacacagcaggcatttgtcccgatgggtagc +f1v4s13 f1v4s13 0.000000 T 143000 gaatcagcttctcaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactggacgattc +f10v4s81 f10v4s81 0.000000 T 409687 ttgccggacactggcaatcttccatagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v5s82 f10v4s81 3.000000 F 409687 ttgccggacactcgcaatcttccaagcagaggtccagctaacacatagcaggcatttagtcccgatgggtagc +f4v8s52 f4v8s52 0.000000 T 161072 ctgatattcgtattgatctactacgtatcaaagtatcccacttttgtgaccccactaagagaaga +f2v11s29 f2v8s26 3.000000 F 863655 tggactgtagaaccgtgtgcctcaatccacgcgtgaggacacgttgatttcttatacggcgtctcag +f0v6s6 f0v6s6 0.000000 T 84418 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f1v3s12 f0v6s6 0.000000 F 84418 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f0v0s0 f1v5s14 3.000000 F 203637 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f0v2s2 f1v5s14 3.000000 F 203637 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f1v7s16 f1v7s16 0.000000 T 81657 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f2v5s23 f1v7s16 0.000000 F 81657 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f3v12s43 f3v3s34 4.000000 F 670881 cgaaaaaggtgaatgaaagactgggacgcctcctggtttgggacgctctaggtggttgtggcttgaacgttagcacct +f8v0s68 f8v0s68 0.000000 T 433190 aagaccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f2v1s19 f2v1s19 0.000000 T 79492 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f1v8s17 f2v1s19 0.000000 F 79492 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f10v11s88 f10v4s81 4.000000 F 409687 ttgccggacactcgcaatcttccaagcagaggtccagctaactcacagccaggcatttagtcccgatgggtagc +f3v5s36 f3v3s34 2.000000 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttatggcttgaaagttagcacct +f8v7s75 f8v0s68 2.000000 F 433190 aagaccttgaacacctctaagcgaaaattggattccagtcagggaaccataaccacgggtgcgt +f10v12s89 f10v4s81 4.000000 F 409687 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacacgcaggcatttagacccgatgggtagc +f4v2s46 f4v8s52 4.000000 F 161072 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaga +f8v2s70 f8v2s70 0.000000 T 74088 aagaccttgaacaacctctaagccgaaattggaattcgagtcagggaacataaccacgggtgcgat +f11v8s99 f11v8s99 0.000000 T 362595 agcgttagcaaagagcctggttaatctgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f7v0s64 f7v0s64 0.000000 T 114016 gcttcgtgggaaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f11v1s92 f11v8s99 4.000000 F 362595 agcgttagcaaagagcctggttcatcgccctcgaggatcggtggtgatcaagagatgcggcatccgtttc +f3v2s33 f3v3s34 4.000000 F 670881 cgaaaaaaggtaatgaaaggctgggatgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcact +f8v3s71 f8v0s68 3.000000 F 433190 aagaccttgaacacctctaagcgaaattggattcgagtcagcgaacataacacgggtgcgt +f4v3s47 f4v3s47 0.000000 T 198866 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaaagaaga +f10v10s87 f10v10s87 0.000000 T 66794 tctgccggacactcgcaatcttccaagcagaggtccagctaacacacgcaggcatttatccgatgggtagc +f11v0s91 f11v8s99 1.000000 F 362595 agcgttagcaaagagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f8v5s73 f8v0s68 3.000000 F 433190 aagaccttgtacacctctaagcgataaattggattcgagtcagggaacataaccacggatgcgt +f8v4s72 f8v0s68 1.000000 F 433190 aagacttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f3v1s32 f3v3s34 2.000000 F 670881 cgaaaaagagtgaatgaaatggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v4s35 f3v3s34 4.000000 F 670881 cgaaaaaggtgaagaaaggctgggacgctcctggttggcgcgctctaggtgttgtggcttgaacgttagcaccta +f11v6s97 f11v6s97 0.000000 T 58908 agcgttagcaaagaacctggttaatcgccctcgaggatcggtggtggatcggagatgcgggcatcgcgtttc +f4v1s45 f4v3s47 3.000000 F 198866 ctgtatttcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactatagagaaga +f0v5s5 f0v5s5 0.000000 T 54120 ggatcacagtctacactgctcactcccccccggcccctgagtccgaggagagggtgcttcaaagtatgtataccactg +f5v5s58 f5v5s58 0.000000 T 97556 cgcaacgcaccattaagatggtaatcactaaaaaaatcggcctaacctgtccaatcgccactt +f1v0s9 f1v4s13 2.000000 F 143000 gaatcagcttctaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactcgacgattc +f8v6s74 f8v0s68 2.000000 F 433190 aagacccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgccgt +f6v0s60 f6v0s60 0.000000 T 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctacg +f10v8s85 f10v8s85 0.000000 T 46704 ttgccggacactcgaatcttccaagcagaggtccagctaacacacagcaggcatactagtcccgatgggtagc +f10v1s78 f10v4s81 4.000000 F 409687 ttgccggacactcgcaatcctccaagcagaggtccagctaacacacagcaggcatttagtcccgatgaggtagc +f5v4s57 f5v5s58 4.000000 F 97556 cggcaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactat +f3v10s41 f3v3s34 4.000000 F 670881 cgaaaataaggtgaatgaaagggtgggacgctcctagtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v7s38 f3v3s34 2.000000 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttcgtgcttgaacgttagcacct +f10v6s83 f10v4s81 4.000000 F 409687 ttccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcctgatgggtagc +f11v2s93 f11v8s99 3.000000 F 362595 agcgttagcaaagagcctggttaatcgccctcggaggatcggtggtgatcagagatgcgggcatccgttc +f3v9s40 f3v3s34 1.000000 F 670881 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggggttgtggcttgaacgttagcacct +f11v4s95 f11v8s99 4.000000 F 362595 agcgttagcaaagagcctggttaatcccctcgagatcgggtggtgatcagagatgcgggcatccgtttc +f8v1s69 f8v0s68 1.000000 F 433190 aagaccttgaacacctctaagcgaaaattggatatcgagtcagggaacataaccacgggtgcgt +f11v5s96 f11v8s99 4.000000 F 362595 agcgttagcaaagagcctggttaatggccctcgaggatcggtggctgaccagagatgcgggcatccgtttc +f6v2s62 f6v0s60 2.000000 F 100983 catgtcgagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctac +f4v6s50 f4v3s47 3.000000 F 198866 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagacgaagag +f9v0s76 f9v0s76 0.000000 T 29993 gggatcgtggcaagcaacccttgccgcctcgactgcggctaaagacgcatgttccagtaataccta +f2v12s30 f2v8s26 3.000000 F 863655 tggactagtagaccgtgtgccacaatcgacgctgaggacacgttgatttttatacggcgtctcag +f11v9s100 f11v8s99 2.000000 F 362595 agcgttagcaaaggagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f4v5s49 f4v3s47 3.000000 F 198866 ctatattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaagca +f2v9s27 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f7v3s67 f7v0s64 1.000000 F 114016 gcttcgtgggaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f0v8s8 f1v5s14 4.000000 F 203637 ggatcacagtcctacactgctcactccaaccccggccctgagtccgaggagagggtgcttcagagtatgtataccactg +f2v6s24 f2v8s26 4.000000 F 863655 tggactagtagacgtgtgcctcatcgacgcgtgagacactttgatttcttatacggcgtctcag +f11v3s94 f11v8s99 3.000000 F 362595 agcgttagcaaagagcctggttaatcgccgtcgaaggatcggtggtgatcagagatgcgggcatccgtttc +f6v3s63 f6v0s60 3.000000 F 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggttttgggtcacgtctccatggctacg +f3v11s42 f3v3s34 2.000000 F 670881 cgaaaaaggtgaatgaaagtctgggacgctcctggtttgggacgctctaggtgttgtggcttgaacgttagcacct +f4v4s48 f4v3s47 2.000000 F 198866 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaa +f3v8s39 f3v3s34 4.000000 F 670881 cgaaaaaggtgaatgaaaggcctgggacgctcctggttggggcgctctaggtgtgtggcttgaacgttagacct +f7v1s65 f7v0s64 2.000000 F 114016 gcttcggggaaccagagacaaccgacatatttcgaaccaccgacatacgatcctttaaacc +f5v1s54 f5v5s58 3.000000 F 97556 cggaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactt +f4v7s51 f4v7s51 0.000000 T 8599 cgtttattcgtaggatctactacgcatcaaagtatcccacttttgtaccccactaagagcaga +f7v2s66 f7v0s64 3.000000 F 114016 gcttcgtgggaaccagaacaaccgacatattccgaaccaccggacatacgatccttaaacc +f1v6s15 f1v4s13 4.000000 F 143000 gaatcagcttctaaccaagcgttgcagaaccggctactttaagcatgatgaatgcgtcgtaagtgatactcgacgattc +f5v2s55 f5v2s55 0.000000 T 666 cggcatcgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccact diff --git a/pkg/obitools/obiclust/testdata/families_rawlcs.expected b/pkg/obitools/obiclust/testdata/families_rawlcs.expected new file mode 100644 index 0000000..9fabe7e --- /dev/null +++ b/pkg/obitools/obiclust/testdata/families_rawlcs.expected @@ -0,0 +1,101 @@ +f2v8s26 f2v8s26 67.000000 T 4505217 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v3s80 f2v8s26 67.000000 F 4505217 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v0s18 f2v8s26 67.000000 F 4505217 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v7s25 f2v8s26 67.000000 F 4505217 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v9s86 f2v8s26 67.000000 F 4505217 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v3s21 f2v8s26 66.000000 F 4505217 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f6v1s61 f2v8s26 66.000000 F 4505217 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f2v2s20 f2v8s26 66.000000 F 4505217 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f3v3s34 f2v8s26 42.000000 F 4505217 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f4v0s44 f2v8s26 42.000000 F 4505217 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v0s31 f2v8s26 42.000000 F 4505217 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f0v7s7 f2v8s26 48.000000 F 4505217 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v2s11 f2v8s26 48.000000 F 4505217 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v1s10 f2v8s26 48.000000 F 4505217 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f3v6s37 f2v8s26 43.000000 F 4505217 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f5v0s53 f2v8s26 43.000000 F 4505217 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f11v7s98 f2v8s26 43.000000 F 4505217 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f2v4s22 f2v8s26 66.000000 F 4505217 tggactagtagaccgtgtcgcctcaatcgacgcgtgaggacacgttgattcttatacggcgtctcag +f10v7s84 f2v8s26 43.000000 F 4505217 gtgccggacactcgcaatcttcacaagcagaggtccagctaacacacagcagcatttagtcccgatgggtagc +f10v0s77 f2v8s26 43.000000 F 4505217 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f5v6s59 f2v8s26 66.000000 F 4505217 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v10s28 f2v8s26 66.000000 F 4505217 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f1v5s14 f2v8s26 46.000000 F 4505217 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v4s4 f2v8s26 46.000000 F 4505217 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v1s1 f2v8s26 46.000000 F 4505217 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v3s3 f2v8s26 46.000000 F 4505217 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f5v3s56 f5v3s56 63.000000 T 229172 ccgcaacgcaccataagatgtaatcactaagaaaaatcggcctaacctgtcatatcgccactt +f10v2s79 f2v8s26 43.000000 F 4505217 ttgccggtcactcgcaatcttaccaaccagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v13s90 f2v8s26 43.000000 F 4505217 tgccggacactcgcaatctccaagcagagagtccaagctaacacacagcaggcatttgtcccgatgggtagc +f1v4s13 f2v8s26 49.000000 F 4505217 gaatcagcttctcaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactggacgattc +f10v4s81 f2v8s26 44.000000 F 4505217 ttgccggacactggcaatcttccatagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v5s82 f2v8s26 43.000000 F 4505217 ttgccggacactcgcaatcttccaagcagaggtccagctaacacatagcaggcatttagtcccgatgggtagc +f4v8s52 f4v8s52 65.000000 T 254973 ctgatattcgtattgatctactacgtatcaaagtatcccacttttgtgaccccactaagagaaga +f2v11s29 f2v8s26 65.000000 F 4505217 tggactgtagaaccgtgtgcctcaatccacgcgtgaggacacgttgatttcttatacggcgtctcag +f0v6s6 f2v8s26 47.000000 F 4505217 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f1v3s12 f2v8s26 47.000000 F 4505217 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f0v0s0 f2v8s26 47.000000 F 4505217 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f0v2s2 f2v8s26 47.000000 F 4505217 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f1v7s16 f2v8s26 46.000000 F 4505217 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f2v5s23 f2v8s26 46.000000 F 4505217 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f3v12s43 f2v8s26 44.000000 F 4505217 cgaaaaaggtgaatgaaagactgggacgcctcctggtttgggacgctctaggtggttgtggcttgaacgttagcacct +f8v0s68 f2v8s26 40.000000 F 4505217 aagaccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f2v1s19 f2v8s26 47.000000 F 4505217 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f1v8s17 f2v8s26 47.000000 F 4505217 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f10v11s88 f2v8s26 43.000000 F 4505217 ttgccggacactcgcaatcttccaagcagaggtccagctaactcacagccaggcatttagtcccgatgggtagc +f3v5s36 f2v8s26 43.000000 F 4505217 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttatggcttgaaagttagcacct +f8v7s75 f5v3s56 41.000000 F 229172 aagaccttgaacacctctaagcgaaaattggattccagtcagggaaccataaccacgggtgcgt +f10v12s89 f2v8s26 43.000000 F 4505217 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacacgcaggcatttagacccgatgggtagc +f4v2s46 f4v8s52 61.000000 F 254973 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaga +f8v2s70 f2v8s26 40.000000 F 4505217 aagaccttgaacaacctctaagccgaaattggaattcgagtcagggaacataaccacgggtgcgat +f11v8s99 f2v8s26 45.000000 F 4505217 agcgttagcaaagagcctggttaatctgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f7v0s64 f7v0s64 61.000000 T 114016 gcttcgtgggaaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f11v1s92 f2v8s26 45.000000 F 4505217 agcgttagcaaagagcctggttcatcgccctcgaggatcggtggtgatcaagagatgcggcatccgtttc +f3v2s33 f2v8s26 42.000000 F 4505217 cgaaaaaaggtaatgaaaggctgggatgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcact +f8v3s71 f2v8s26 40.000000 F 4505217 aagaccttgaacacctctaagcgaaattggattcgagtcagcgaacataacacgggtgcgt +f4v3s47 f4v8s52 60.000000 F 254973 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaaagaaga +f10v10s87 f2v8s26 43.000000 F 4505217 tctgccggacactcgcaatcttccaagcagaggtccagctaacacacgcaggcatttatccgatgggtagc +f11v0s91 f2v8s26 45.000000 F 4505217 agcgttagcaaagagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f8v5s73 f2v8s26 40.000000 F 4505217 aagaccttgtacacctctaagcgataaattggattcgagtcagggaacataaccacggatgcgt +f8v4s72 f8v4s72 62.000000 T 59724 aagacttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f3v1s32 f2v8s26 44.000000 F 4505217 cgaaaaagagtgaatgaaatggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v4s35 f2v8s26 43.000000 F 4505217 cgaaaaaggtgaagaaaggctgggacgctcctggttggcgcgctctaggtgttgtggcttgaacgttagcaccta +f11v6s97 f2v8s26 45.000000 F 4505217 agcgttagcaaagaacctggttaatcgccctcgaggatcggtggtggatcggagatgcgggcatcgcgtttc +f4v1s45 f2v8s26 40.000000 F 4505217 ctgtatttcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactatagagaaga +f0v5s5 f2v8s26 45.000000 F 4505217 ggatcacagtctacactgctcactcccccccggcccctgagtccgaggagagggtgcttcaaagtatgtataccactg +f5v5s58 f5v3s56 60.000000 F 229172 cgcaacgcaccattaagatggtaatcactaaaaaaatcggcctaacctgtccaatcgccactt +f1v0s9 f2v8s26 47.000000 F 4505217 gaatcagcttctaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactcgacgattc +f8v6s74 f2v8s26 41.000000 F 4505217 aagacccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgccgt +f6v0s60 f2v8s26 41.000000 F 4505217 catgtcaagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctacg +f10v8s85 f2v8s26 43.000000 F 4505217 ttgccggacactcgaatcttccaagcagaggtccagctaacacacagcaggcatactagtcccgatgggtagc +f10v1s78 f2v8s26 44.000000 F 4505217 ttgccggacactcgcaatcctccaagcagaggtccagctaacacacagcaggcatttagtcccgatgaggtagc +f5v4s57 f2v8s26 41.000000 F 4505217 cggcaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactat +f3v10s41 f2v8s26 43.000000 F 4505217 cgaaaataaggtgaatgaaagggtgggacgctcctagtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v7s38 f2v8s26 43.000000 F 4505217 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttcgtgcttgaacgttagcacct +f10v6s83 f2v8s26 44.000000 F 4505217 ttccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcctgatgggtagc +f11v2s93 f2v8s26 45.000000 F 4505217 agcgttagcaaagagcctggttaatcgccctcggaggatcggtggtgatcagagatgcgggcatccgttc +f3v9s40 f2v8s26 42.000000 F 4505217 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggggttgtggcttgaacgttagcacct +f11v4s95 f2v8s26 44.000000 F 4505217 agcgttagcaaagagcctggttaatcccctcgagatcgggtggtgatcagagatgcgggcatccgtttc +f8v1s69 f2v8s26 40.000000 F 4505217 aagaccttgaacacctctaagcgaaaattggatatcgagtcagggaacataaccacgggtgcgt +f11v5s96 f2v8s26 44.000000 F 4505217 agcgttagcaaagagcctggttaatggccctcgaggatcggtggctgaccagagatgcgggcatccgtttc +f6v2s62 f2v8s26 40.000000 F 4505217 catgtcgagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctac +f4v6s50 f2v8s26 40.000000 F 4505217 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagacgaagag +f9v0s76 f2v8s26 44.000000 F 4505217 gggatcgtggcaagcaacccttgccgcctcgactgcggctaaagacgcatgttccagtaataccta +f2v12s30 f2v8s26 64.000000 F 4505217 tggactagtagaccgtgtgccacaatcgacgctgaggacacgttgatttttatacggcgtctcag +f11v9s100 f2v8s26 45.000000 F 4505217 agcgttagcaaaggagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f4v5s49 f2v8s26 40.000000 F 4505217 ctatattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaagca +f2v9s27 f2v8s26 66.000000 F 4505217 tggactagtagaccgtgtgcctaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f7v3s67 f7v0s64 60.000000 F 114016 gcttcgtgggaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f0v8s8 f2v8s26 47.000000 F 4505217 ggatcacagtcctacactgctcactccaaccccggccctgagtccgaggagagggtgcttcagagtatgtataccactg +f2v6s24 f2v8s26 63.000000 F 4505217 tggactagtagacgtgtgcctcatcgacgcgtgagacactttgatttcttatacggcgtctcag +f11v3s94 f2v8s26 46.000000 F 4505217 agcgttagcaaagagcctggttaatcgccgtcgaaggatcggtggtgatcagagatgcgggcatccgtttc +f6v3s63 f2v8s26 42.000000 F 4505217 catgtcaagagactggaaccccggtgatgtgaaaaaaggttttgggtcacgtctccatggctacg +f3v11s42 f2v8s26 44.000000 F 4505217 cgaaaaaggtgaatgaaagtctgggacgctcctggtttgggacgctctaggtgttgtggcttgaacgttagcacct +f4v4s48 f4v8s52 60.000000 F 254973 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaa +f3v8s39 f2v8s26 43.000000 F 4505217 cgaaaaaggtgaatgaaaggcctgggacgctcctggttggggcgctctaggtgtgtggcttgaacgttagacct +f7v1s65 f7v0s64 60.000000 F 114016 gcttcggggaaccagagacaaccgacatatttcgaaccaccgacatacgatcctttaaacc +f5v1s54 f5v3s56 60.000000 F 229172 cggaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactt +f4v7s51 f4v8s52 58.000000 F 254973 cgtttattcgtaggatctactacgcatcaaagtatcccacttttgtaccccactaagagcaga +f7v2s66 f7v0s64 59.000000 F 114016 gcttcgtgggaaccagaacaaccgacatattccgaaccaccggacatacgatccttaaacc +f1v6s15 f2v8s26 46.000000 F 4505217 gaatcagcttctaaccaagcgttgcagaaccggctactttaagcatgatgaatgcgtcgtaagtgatactcgacgattc +f5v2s55 f2v8s26 40.000000 F 4505217 cggcatcgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccact diff --git a/pkg/obitools/obiclust/testdata/families_shortest.expected b/pkg/obitools/obiclust/testdata/families_shortest.expected new file mode 100644 index 0000000..8b34382 --- /dev/null +++ b/pkg/obitools/obiclust/testdata/families_shortest.expected @@ -0,0 +1,101 @@ +f2v8s26 f2v8s26 1.000000 T 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v3s80 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v0s18 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v7s25 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f10v9s86 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v3s21 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f6v1s61 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f2v2s20 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctcaatcgacgcggaggacacgttgatttcttatacggcgtctcag +f3v3s34 f3v3s34 1.000000 T 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f4v0s44 f3v3s34 1.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v0s31 f3v3s34 1.000000 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f0v7s7 f0v7s7 1.000000 T 515632 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v2s11 f0v7s7 1.000000 F 515632 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f1v1s10 f0v7s7 1.000000 F 515632 ggatcacacgtctatcactgctcactccaaccccggcccttgacgtccgaggagagggtgcttctgagtatgtatacccaactg +f3v6s37 f3v3s34 0.973333 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f5v0s53 f3v3s34 0.973333 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f11v7s98 f3v3s34 0.973333 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgtctctaggttgttgtggctgaacgtagcact +f2v4s22 f2v8s26 0.985075 F 863655 tggactagtagaccgtgtcgcctcaatcgacgcgtgaggacacgttgattcttatacggcgtctcag +f10v7s84 f10v7s84 1.000000 T 905994 gtgccggacactcgcaatcttcacaagcagaggtccagctaacacacagcagcatttagtcccgatgggtagc +f10v0s77 f10v7s84 0.972603 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f5v6s59 f2v8s26 1.000000 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f2v10s28 f2v8s26 1.000000 F 863655 ggactagtagaccgtgtgcctcaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f1v5s14 f0v7s7 0.961538 F 515632 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v4s4 f0v7s7 0.961538 F 515632 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v1s1 f0v7s7 0.961538 F 515632 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f0v3s3 f0v7s7 0.961538 F 515632 ggatcacagtctacactgctcactccagaccccgcccctgagtccgaggagagggtgcttcagagtatgttaccactg +f5v3s56 f5v3s56 1.000000 T 192648 ccgcaacgcaccataagatgtaatcactaagaaaaatcggcctaacctgtcatatcgccactt +f10v2s79 f10v7s84 0.945205 F 905994 ttgccggtcactcgcaatcttaccaaccagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v13s90 f10v7s84 0.958333 F 905994 tgccggacactcgcaatctccaagcagagagtccaagctaacacacagcaggcatttgtcccgatgggtagc +f1v4s13 f1v4s13 1.000000 T 304149 gaatcagcttctcaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactggacgattc +f10v4s81 f10v7s84 0.958904 F 905994 ttgccggacactggcaatcttccatagcagaggtccagctaacacacagcaggcatttagtcccgatgggtagc +f10v5s82 f10v7s84 0.958904 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacatagcaggcatttagtcccgatgggtagc +f4v8s52 f4v8s52 1.000000 T 368537 ctgatattcgtattgatctactacgtatcaaagtatcccacttttgtgaccccactaagagaaga +f2v11s29 f2v8s26 0.970149 F 863655 tggactgtagaaccgtgtgcctcaatccacgcgtgaggacacgttgatttcttatacggcgtctcag +f0v6s6 f0v7s7 0.961039 F 515632 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f1v3s12 f0v7s7 0.961039 F 515632 ggatcacagtctacactgctccctccaaccccggcccctggtccgaggagagggtgcttcagagtattataccactg +f0v0s0 f0v7s7 0.974684 F 515632 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f0v2s2 f0v7s7 0.974684 F 515632 ggatcacagtctacactgctcactccaaccccggcccctgagtccgaggagagggtgcttcagagtatgtataccactg +f1v7s16 f1v4s13 0.973684 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f2v5s23 f1v4s13 0.973684 F 304149 gaatcagcttcaaccaagcgatgcagaaccgctgctttaagcatgatgaatcgtcgtaagtgatactcgacgattc +f3v12s43 f3v3s34 0.973684 F 838714 cgaaaaaggtgaatgaaagactgggacgcctcctggtttgggacgctctaggtggttgtggcttgaacgttagcacct +f8v0s68 f8v0s68 1.000000 T 507278 aagaccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f2v1s19 f1v4s13 0.925000 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f1v8s17 f1v4s13 0.925000 F 304149 gaatcaggttcttccaagcgctgcagaaccggctactttaagcattgatgaatgcgtcagtaagtgatacccgacgattc +f10v11s88 f10v7s84 0.958904 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaactcacagccaggcatttagtcccgatgggtagc +f3v5s36 f3v3s34 0.973684 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttatggcttgaaagttagcacct +f8v7s75 f8v0s68 0.984127 F 507278 aagaccttgaacacctctaagcgaaaattggattccagtcagggaaccataaccacgggtgcgt +f10v12s89 f10v7s84 0.958904 F 905994 ttgccggacactcgcaatcttccaagcagaggtccagctaacacacacgcaggcatttagacccgatgggtagc +f4v2s46 f4v8s52 0.968254 F 368537 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaga +f8v2s70 f8v0s68 0.984127 F 507278 aagaccttgaacaacctctaagccgaaattggaattcgagtcagggaacataaccacgggtgcgat +f11v8s99 f11v8s99 1.000000 T 421503 agcgttagcaaagagcctggttaatctgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f7v0s64 f7v0s64 1.000000 T 114016 gcttcgtgggaaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f11v1s92 f11v8s99 0.971429 F 421503 agcgttagcaaagagcctggttcatcgccctcgaggatcggtggtgatcaagagatgcggcatccgtttc +f3v2s33 f3v3s34 0.973333 F 838714 cgaaaaaaggtaatgaaaggctgggatgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcact +f8v3s71 f8v0s68 0.983607 F 507278 aagaccttgaacacctctaagcgaaattggattcgagtcagcgaacataacacgggtgcgt +f4v3s47 f4v8s52 0.967742 F 368537 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaaagaaga +f10v10s87 f10v7s84 0.957746 F 905994 tctgccggacactcgcaatcttccaagcagaggtccagctaacacacgcaggcatttatccgatgggtagc +f11v0s91 f11v8s99 1.000000 F 421503 agcgttagcaaagagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f8v5s73 f8v0s68 0.968254 F 507278 aagaccttgtacacctctaagcgataaattggattcgagtcagggaacataaccacggatgcgt +f8v4s72 f8v0s68 1.000000 F 507278 aagacttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgcgt +f3v1s32 f3v3s34 1.000000 F 838714 cgaaaaagagtgaatgaaatggctgggacgctcctggtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v4s35 f3v3s34 0.973333 F 838714 cgaaaaaggtgaagaaaggctgggacgctcctggttggcgcgctctaggtgttgtggcttgaacgttagcaccta +f11v6s97 f11v8s99 0.957746 F 421503 agcgttagcaaagaacctggttaatcgccctcgaggatcggtggtggatcggagatgcgggcatcgcgtttc +f4v1s45 f4v8s52 0.938462 F 368537 ctgtatttcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactatagagaaga +f0v5s5 f0v7s7 0.948718 F 515632 ggatcacagtctacactgctcactcccccccggcccctgagtccgaggagagggtgcttcaaagtatgtataccactg +f5v5s58 f5v3s56 0.952381 F 192648 cgcaacgcaccattaagatggtaatcactaaaaaaatcggcctaacctgtccaatcgccactt +f1v0s9 f1v4s13 0.987500 F 304149 gaatcagcttctaaccaagcgatgcagaaccggctactttaagcattgatgaatgcgtcgtaagtgatactcgacgattc +f8v6s74 f8v0s68 1.000000 F 507278 aagacccttgaacacctctaagcgaaaattggattcgagtcagggaacataaccacgggtgccgt +f6v0s60 f6v0s60 1.000000 T 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctacg +f10v8s85 f10v7s84 0.945205 F 905994 ttgccggacactcgaatcttccaagcagaggtccagctaacacacagcaggcatactagtcccgatgggtagc +f10v1s78 f10v7s84 0.958904 F 905994 ttgccggacactcgcaatcctccaagcagaggtccagctaacacacagcaggcatttagtcccgatgaggtagc +f5v4s57 f5v3s56 0.968254 F 192648 cggcaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactat +f3v10s41 f3v3s34 0.973684 F 838714 cgaaaataaggtgaatgaaagggtgggacgctcctagtttggggcgctctaggtgttgtggcttgaacgttagcacct +f3v7s38 f3v3s34 0.986842 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggtgttcgtgcttgaacgttagcacct +f10v6s83 f10v7s84 0.958333 F 905994 ttccggacactcgcaatcttccaagcagaggtccagctaacacacagcaggcatttagtcctgatgggtagc +f11v2s93 f11v8s99 0.985714 F 421503 agcgttagcaaagagcctggttaatcgccctcggaggatcggtggtgatcagagatgcgggcatccgttc +f3v9s40 f3v3s34 0.986842 F 838714 cgaaaaaggtgaatgaaaggctgggacgctcctggtttggggcgctctaggggttgtggcttgaacgttagcacct +f11v4s95 f11v8s99 0.985507 F 421503 agcgttagcaaagagcctggttaatcccctcgagatcgggtggtgatcagagatgcgggcatccgtttc +f8v1s69 f8v0s68 1.000000 F 507278 aagaccttgaacacctctaagcgaaaattggatatcgagtcagggaacataaccacgggtgcgt +f11v5s96 f11v8s99 0.957746 F 421503 agcgttagcaaagagcctggttaatggccctcgaggatcggtggctgaccagagatgcgggcatccgtttc +f6v2s62 f6v0s60 0.983871 F 100983 catgtcgagagactggaaccccggtgatgtgaaaaaaggtcttgggtacgtcccatggctac +f4v6s50 f4v8s52 0.938462 F 368537 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagacgaagag +f9v0s76 f9v0s76 1.000000 T 29993 gggatcgtggcaagcaacccttgccgcctcgactgcggctaaagacgcatgttccagtaataccta +f2v12s30 f2v8s26 0.984615 F 863655 tggactagtagaccgtgtgccacaatcgacgctgaggacacgttgatttttatacggcgtctcag +f11v9s100 f11v8s99 0.985915 F 421503 agcgttagcaaaggagcctggttaatcgccctcgaggatcggtggtgatcagagatgcgggcatccgtttc +f4v5s49 f4v8s52 0.953125 F 368537 ctatattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaagca +f2v9s27 f2v8s26 1.000000 F 863655 tggactagtagaccgtgtgcctaatcgacgcgtgaggacacgttgatttcttatacggcgtctcag +f7v3s67 f7v0s64 1.000000 F 114016 gcttcgtgggaccagagacaaccgacatatttcgaaccaccgacatacgatccttaaacc +f0v8s8 f0v7s7 0.974684 F 515632 ggatcacagtcctacactgctcactccaaccccggccctgagtccgaggagagggtgcttcagagtatgtataccactg +f2v6s24 f2v8s26 0.984375 F 863655 tggactagtagacgtgtgcctcatcgacgcgtgagacactttgatttcttatacggcgtctcag +f11v3s94 f11v8s99 0.971831 F 421503 agcgttagcaaagagcctggttaatcgccgtcgaaggatcggtggtgatcagagatgcgggcatccgtttc +f6v3s63 f6v0s60 0.984127 F 100983 catgtcaagagactggaaccccggtgatgtgaaaaaaggttttgggtcacgtctccatggctacg +f3v11s42 f3v3s34 0.973684 F 838714 cgaaaaaggtgaatgaaagtctgggacgctcctggtttgggacgctctaggtgttgtggcttgaacgttagcacct +f4v4s48 f4v8s52 0.967742 F 368537 ctgtattcgtagtgatctactacgcatcaaagtatcccacttttgtaccccactaagagaaa +f3v8s39 f3v3s34 0.986486 F 838714 cgaaaaaggtgaatgaaaggcctgggacgctcctggttggggcgctctaggtgtgtggcttgaacgttagacct +f7v1s65 f7v0s64 0.983607 F 114016 gcttcggggaaccagagacaaccgacatatttcgaaccaccgacatacgatcctttaaacc +f5v1s54 f5v3s56 0.952381 F 192648 cggaacgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccactt +f4v7s51 f4v8s52 0.920635 F 368537 cgtttattcgtaggatctactacgcatcaaagtatcccacttttgtaccccactaagagcaga +f7v2s66 f7v0s64 0.967213 F 114016 gcttcgtgggaaccagaacaaccgacatattccgaaccaccggacatacgatccttaaacc +f1v6s15 f1v4s13 0.974684 F 304149 gaatcagcttctaaccaagcgttgcagaaccggctactttaagcatgatgaatgcgtcgtaagtgatactcgacgattc +f5v2s55 f5v3s56 0.936508 F 192648 cggcatcgcaccataagatggtaatcactaaaaaaatcggcctaacctgtccatatcgccact