fix issue in WFBacktrace and change format to proper CIGAR,
add test to ensure CIGAR correctness in the case of different traceback results, add DecodeCIGAR function to exports
This commit is contained in:
@@ -3,6 +3,7 @@ package tests
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"strconv"
|
||||
@@ -46,6 +47,71 @@ func TestWavefrontPacking(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func GetScoreFromCIGAR(CIGAR string, penalties wfa.Penalty) int {
|
||||
unpackedCIGAR := wfa.RunLengthDecode(CIGAR)
|
||||
previousOp := '~'
|
||||
score := 0
|
||||
for _, Op := range unpackedCIGAR {
|
||||
if Op == 'M' {
|
||||
score = score + penalties.M
|
||||
} else if Op == 'X' {
|
||||
score = score + penalties.X
|
||||
} else if (Op == 'I' && previousOp != 'I') || (Op == 'D' && previousOp != 'D') {
|
||||
score = score + penalties.O + penalties.E
|
||||
} else if (Op == 'I' && previousOp == 'I') || (Op == 'D' && previousOp == 'D') {
|
||||
score = score + penalties.E
|
||||
}
|
||||
previousOp = Op
|
||||
}
|
||||
return score
|
||||
}
|
||||
|
||||
func CheckCIGARCorrectness(s1 string, s2 string, CIGAR string) bool {
|
||||
unpackedCIGAR := wfa.RunLengthDecode(CIGAR)
|
||||
i := 0
|
||||
j := 0
|
||||
|
||||
s1Aligned := strings.Builder{}
|
||||
alignment := strings.Builder{}
|
||||
s2Aligned := strings.Builder{}
|
||||
|
||||
for c := 0; c < len(unpackedCIGAR); c++ {
|
||||
Op := unpackedCIGAR[c]
|
||||
if Op == 'M' {
|
||||
s1Aligned.WriteByte(s1[i])
|
||||
alignment.WriteRune('|')
|
||||
s2Aligned.WriteByte(s2[j])
|
||||
i++
|
||||
j++
|
||||
} else if Op == 'X' {
|
||||
s1Aligned.WriteByte(s1[i])
|
||||
alignment.WriteRune(' ')
|
||||
s2Aligned.WriteByte(s2[j])
|
||||
i++
|
||||
j++
|
||||
} else if Op == 'I' {
|
||||
|
||||
s1Aligned.WriteRune('-')
|
||||
alignment.WriteRune(' ')
|
||||
s2Aligned.WriteByte(s2[j])
|
||||
|
||||
j++
|
||||
} else if Op == 'D' {
|
||||
s1Aligned.WriteByte(s1[i])
|
||||
alignment.WriteRune('|')
|
||||
s2Aligned.WriteRune('-')
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(s1) && j == len(s2) {
|
||||
return true
|
||||
} else {
|
||||
log.Printf("\n%s\n%s\n%s\n i=%d, j=%d, |s1|=%d, |s2|=%d\n", s1Aligned.String(), alignment.String(), s2Aligned.String(), i, j, len(s1), len(s2))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func TestWFA(t *testing.T) {
|
||||
content, _ := os.ReadFile(testJsonPath)
|
||||
|
||||
@@ -73,7 +139,9 @@ func TestWFA(t *testing.T) {
|
||||
|
||||
for solutions.Scan() {
|
||||
solution := solutions.Text()
|
||||
|
||||
expectedScore, _ := strconv.Atoi(strings.Split(solution, "\t")[0])
|
||||
expectedCIGAR := strings.Split(solution, "\t")[1]
|
||||
|
||||
sequences.Scan()
|
||||
s1 := sequences.Text()
|
||||
@@ -85,9 +153,25 @@ func TestWFA(t *testing.T) {
|
||||
|
||||
x := wfa.WFAlign(s1, s2, testPenalties, true)
|
||||
gotScore := x.Score
|
||||
gotCIGAR := x.CIGAR
|
||||
|
||||
if gotScore != -1*expectedScore {
|
||||
t.Errorf(`test: %s#%d, s1: %s, s2: %s, got: %d, expected: %d\n`, testName, idx, s1, s2, gotScore, expectedScore)
|
||||
t.Errorf(`test: %s#%d, s1: %s, s2: %s, got: %d, expected: %d`, testName, idx, s1, s2, gotScore, expectedScore)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if gotCIGAR != expectedCIGAR {
|
||||
checkScore := GetScoreFromCIGAR(gotCIGAR, testPenalties)
|
||||
CIGARCorrectness := CheckCIGARCorrectness(s1, s2, gotCIGAR)
|
||||
if checkScore != gotScore && checkScore != -1*expectedScore { // nonequivalent alignment
|
||||
t.Errorf(`test: %s#%d, s1: %s, s2: %s, got: [%s], expected: [%s]`, testName, idx, s1, s2, gotCIGAR, expectedCIGAR)
|
||||
t.Errorf(`test: %s#%d, recalculated score: %d`, testName, idx, checkScore)
|
||||
os.Exit(1)
|
||||
}
|
||||
if !CIGARCorrectness {
|
||||
t.Errorf(`test: %s#%d, s1: %s, s2: %s, got: [%s], expected: [%s]`, testName, idx, s1, s2, gotCIGAR, expectedCIGAR)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
idx++
|
||||
|
Reference in New Issue
Block a user