update go mod,
move Wavefront String method to debug file, minor optimizations
This commit is contained in:
parent
2c7adbef06
commit
8679c51fb0
6
go.mod
6
go.mod
@ -2,10 +2,14 @@ module wfa
|
|||||||
|
|
||||||
go 1.23.2
|
go 1.23.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/schollz/progressbar/v3 v3.17.0
|
||||||
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/schollz/progressbar/v3 v3.16.1 // indirect
|
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
golang.org/x/sys v0.26.0 // indirect
|
||||||
golang.org/x/term v0.25.0 // indirect
|
golang.org/x/term v0.25.0 // indirect
|
||||||
)
|
)
|
||||||
|
@ -1,63 +1,59 @@
|
|||||||
package wfa
|
package wfa
|
||||||
|
|
||||||
type IntegerSlice[T any] struct {
|
import (
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Wavefront[T constraints.Integer] struct { // since wavefronts store diag distance, they should never be negative, and traceback data can be stored as uint8
|
||||||
data []T
|
data []T
|
||||||
valid []bool
|
valid []bool
|
||||||
defaultValue T
|
lo int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *IntegerSlice[T]) TranslateIndex(idx int) int {
|
func NewWavefront[T constraints.Integer](lo int, hi int) *Wavefront[T] {
|
||||||
if idx >= 0 { // 0 -> 0, 1 -> 2, 2 -> 4, 3 -> 6, ...
|
a := &Wavefront[T]{}
|
||||||
return 2 * idx
|
|
||||||
} else { // -1 -> 1, -2 -> 3, -3 -> 5, ...
|
|
||||||
return (-2 * idx) - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *IntegerSlice[T]) Valid(idx int) bool {
|
a.lo = lo
|
||||||
actualIdx := a.TranslateIndex(idx)
|
size := a.TranslateIndex(hi)
|
||||||
return 0 <= actualIdx && actualIdx < len(a.valid) && a.valid[actualIdx]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *IntegerSlice[T]) Get(idx int) T {
|
|
||||||
actualIdx := a.TranslateIndex(idx)
|
|
||||||
if 0 <= actualIdx && actualIdx < len(a.valid) && a.valid[actualIdx] { // idx is in the slice
|
|
||||||
return a.data[actualIdx]
|
|
||||||
} else { // idx is out of the slice
|
|
||||||
return a.defaultValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *IntegerSlice[T]) Set(idx int, value T) {
|
|
||||||
actualIdx := a.TranslateIndex(idx)
|
|
||||||
if actualIdx >= len(a.valid) { // idx is outside the slice
|
|
||||||
// expand data array to actualIdx
|
|
||||||
newData := make([]T, 2*actualIdx+1)
|
|
||||||
copy(newData, a.data)
|
|
||||||
a.data = newData
|
|
||||||
|
|
||||||
// expand valid array to actualIdx
|
|
||||||
newValid := make([]bool, 2*actualIdx+1)
|
|
||||||
copy(newValid, a.valid)
|
|
||||||
a.valid = newValid
|
|
||||||
}
|
|
||||||
|
|
||||||
a.data[actualIdx] = value
|
|
||||||
a.valid[actualIdx] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *IntegerSlice[T]) Preallocate(lo int, hi int) {
|
|
||||||
actualLo := a.TranslateIndex(lo)
|
|
||||||
actualHi := a.TranslateIndex(hi)
|
|
||||||
size := max(actualHi, actualLo)
|
|
||||||
|
|
||||||
// expand data array to actualIdx
|
|
||||||
newData := make([]T, size+1)
|
newData := make([]T, size+1)
|
||||||
a.data = newData
|
a.data = newData
|
||||||
|
|
||||||
// expand valid array to actualIdx
|
|
||||||
newValid := make([]bool, size+1)
|
newValid := make([]bool, size+1)
|
||||||
a.valid = newValid
|
a.valid = newValid
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Wavefront[T]) TranslateIndex(idx int) int {
|
||||||
|
return idx - a.lo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Wavefront[T]) Valid(idx int) bool {
|
||||||
|
actualIdx := a.TranslateIndex(idx)
|
||||||
|
return 0 <= actualIdx && actualIdx < len(a.data) && a.valid[actualIdx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Wavefront[T]) Get(idx int) T {
|
||||||
|
actualIdx := a.TranslateIndex(idx)
|
||||||
|
if 0 <= actualIdx && actualIdx < len(a.data) { // idx is in the slice
|
||||||
|
return a.data[actualIdx]
|
||||||
|
} else { // idx is out of the slice
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Wavefront[T]) Set(idx int, value T) {
|
||||||
|
actualIdx := a.TranslateIndex(idx)
|
||||||
|
|
||||||
|
/* in theory idx is always in bounds because the wavefront is preallocated
|
||||||
|
if actualIdx < 0 || actualIdx >= len(a.data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
a.data[actualIdx] = value
|
||||||
|
a.valid[actualIdx] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
type PositiveSlice[T any] struct {
|
type PositiveSlice[T any] struct {
|
||||||
|
79
pkg/debug.go
Normal file
79
pkg/debug.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//go:build debug
|
||||||
|
|
||||||
|
package wfa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (w *WavefrontComponent) String(score int) string {
|
||||||
|
traceback_str := []string{"OI", "EI", "OD", "ED", "SB", "IN", "DL", "EN"}
|
||||||
|
s := "<"
|
||||||
|
min_lo := math.MaxInt
|
||||||
|
max_hi := math.MinInt
|
||||||
|
|
||||||
|
for i := 0; i <= score; i++ {
|
||||||
|
if w.lo.Valid(i) && w.lo.Get(i) < min_lo {
|
||||||
|
min_lo = w.lo.Get(i)
|
||||||
|
}
|
||||||
|
if w.hi.Valid(i) && w.hi.Get(i) > max_hi {
|
||||||
|
max_hi = w.hi.Get(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := min_lo; k <= max_hi; k++ {
|
||||||
|
s = s + fmt.Sprintf("%02d", k)
|
||||||
|
if k < max_hi {
|
||||||
|
s = s + "|"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s = s + ">\t<"
|
||||||
|
|
||||||
|
for k := min_lo; k <= max_hi; k++ {
|
||||||
|
s = s + fmt.Sprintf("%02d", k)
|
||||||
|
if k < max_hi {
|
||||||
|
s = s + "|"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s = s + ">\n"
|
||||||
|
|
||||||
|
for i := 0; i <= score; i++ {
|
||||||
|
s = s + "["
|
||||||
|
lo := w.lo.Get(i)
|
||||||
|
hi := w.hi.Get(i)
|
||||||
|
// print out wavefront matrix
|
||||||
|
for k := min_lo; k <= max_hi; k++ {
|
||||||
|
if w.W.Valid(i) && w.W.Get(i).Valid(k) {
|
||||||
|
s = s + fmt.Sprintf("%02d", w.W.Get(i).Get(k))
|
||||||
|
} else if k < lo || k > hi {
|
||||||
|
s = s + "--"
|
||||||
|
} else {
|
||||||
|
s = s + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
if k < max_hi {
|
||||||
|
s = s + "|"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = s + "]\t["
|
||||||
|
// print out traceback matrix
|
||||||
|
for k := min_lo; k <= max_hi; k++ {
|
||||||
|
if w.A.Valid(i) && w.A.Get(i).Valid(k) {
|
||||||
|
s = s + traceback_str[w.A.Get(i).Get(k)]
|
||||||
|
} else if k < lo || k > hi {
|
||||||
|
s = s + "--"
|
||||||
|
} else {
|
||||||
|
s = s + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
if k < max_hi {
|
||||||
|
s = s + "|"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = s + "]\n"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
104
pkg/types.go
104
pkg/types.go
@ -1,10 +1,5 @@
|
|||||||
package wfa
|
package wfa
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Score int
|
Score int
|
||||||
CIGAR string
|
CIGAR string
|
||||||
@ -33,8 +28,8 @@ const (
|
|||||||
type WavefrontComponent struct {
|
type WavefrontComponent struct {
|
||||||
lo *PositiveSlice[int] // lo for each wavefront
|
lo *PositiveSlice[int] // lo for each wavefront
|
||||||
hi *PositiveSlice[int] // hi for each wavefront
|
hi *PositiveSlice[int] // hi for each wavefront
|
||||||
W *PositiveSlice[*IntegerSlice[int]] // wavefront diag distance for each wavefront
|
W *PositiveSlice[*Wavefront[int]] // wavefront diag distance for each wavefront
|
||||||
A *PositiveSlice[*IntegerSlice[traceback]] // compact CIGAR for backtrace for each wavefront
|
A *PositiveSlice[*Wavefront[traceback]] // compact CIGAR for backtrace for each wavefront
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWavefrontComponent(preallocateSize int) WavefrontComponent {
|
func NewWavefrontComponent(preallocateSize int) WavefrontComponent {
|
||||||
@ -53,16 +48,16 @@ func NewWavefrontComponent(preallocateSize int) WavefrontComponent {
|
|||||||
data: []int{0},
|
data: []int{0},
|
||||||
valid: []bool{true},
|
valid: []bool{true},
|
||||||
},
|
},
|
||||||
W: &PositiveSlice[*IntegerSlice[int]]{
|
W: &PositiveSlice[*Wavefront[int]]{
|
||||||
defaultValue: &IntegerSlice[int]{
|
defaultValue: &Wavefront[int]{
|
||||||
data: []int{},
|
data: []int{0},
|
||||||
valid: []bool{},
|
valid: []bool{false},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
A: &PositiveSlice[*IntegerSlice[traceback]]{
|
A: &PositiveSlice[*Wavefront[traceback]]{
|
||||||
defaultValue: &IntegerSlice[traceback]{
|
defaultValue: &Wavefront[traceback]{
|
||||||
data: []traceback{},
|
data: []traceback{0},
|
||||||
valid: []bool{},
|
valid: []bool{false},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -114,81 +109,10 @@ func (w *WavefrontComponent) SetLoHi(score int, lo int, hi int) {
|
|||||||
w.hi.Set(score, hi)
|
w.hi.Set(score, hi)
|
||||||
|
|
||||||
// preemptively setup w.A
|
// preemptively setup w.A
|
||||||
w.A.Set(score, &IntegerSlice[traceback]{})
|
a := NewWavefront[traceback](lo, hi)
|
||||||
w.A.Get(score).Preallocate(lo, hi)
|
w.A.Set(score, a)
|
||||||
|
|
||||||
// preemptively setup w.W
|
// preemptively setup w.W
|
||||||
w.W.Set(score, &IntegerSlice[int]{})
|
b := NewWavefront[int](lo, hi)
|
||||||
w.W.Get(score).Preallocate(lo, hi)
|
w.W.Set(score, b)
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WavefrontComponent) String(score int) string {
|
|
||||||
traceback_str := []string{"OI", "EI", "OD", "ED", "SB", "IN", "DL", "EN"}
|
|
||||||
s := "<"
|
|
||||||
min_lo := math.MaxInt
|
|
||||||
max_hi := math.MinInt
|
|
||||||
|
|
||||||
for i := 0; i <= score; i++ {
|
|
||||||
if w.lo.Valid(i) && w.lo.Get(i) < min_lo {
|
|
||||||
min_lo = w.lo.Get(i)
|
|
||||||
}
|
|
||||||
if w.hi.Valid(i) && w.hi.Get(i) > max_hi {
|
|
||||||
max_hi = w.hi.Get(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k := min_lo; k <= max_hi; k++ {
|
|
||||||
s = s + fmt.Sprintf("%02d", k)
|
|
||||||
if k < max_hi {
|
|
||||||
s = s + "|"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s = s + ">\t<"
|
|
||||||
|
|
||||||
for k := min_lo; k <= max_hi; k++ {
|
|
||||||
s = s + fmt.Sprintf("%02d", k)
|
|
||||||
if k < max_hi {
|
|
||||||
s = s + "|"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s = s + ">\n"
|
|
||||||
|
|
||||||
for i := 0; i <= score; i++ {
|
|
||||||
s = s + "["
|
|
||||||
lo := w.lo.Get(i)
|
|
||||||
hi := w.hi.Get(i)
|
|
||||||
// print out wavefront matrix
|
|
||||||
for k := min_lo; k <= max_hi; k++ {
|
|
||||||
if w.W.Valid(i) && w.W.Get(i).Valid(k) {
|
|
||||||
s = s + fmt.Sprintf("%02d", w.W.Get(i).Get(k))
|
|
||||||
} else if k < lo || k > hi {
|
|
||||||
s = s + "--"
|
|
||||||
} else {
|
|
||||||
s = s + " "
|
|
||||||
}
|
|
||||||
|
|
||||||
if k < max_hi {
|
|
||||||
s = s + "|"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s = s + "]\t["
|
|
||||||
// print out traceback matrix
|
|
||||||
for k := min_lo; k <= max_hi; k++ {
|
|
||||||
if w.A.Valid(i) && w.A.Get(i).Valid(k) {
|
|
||||||
s = s + traceback_str[w.A.Get(i).Get(k)]
|
|
||||||
} else if k < lo || k > hi {
|
|
||||||
s = s + "--"
|
|
||||||
} else {
|
|
||||||
s = s + " "
|
|
||||||
}
|
|
||||||
|
|
||||||
if k < max_hi {
|
|
||||||
s = s + "|"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s = s + "]\n"
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ func TestWFA(t *testing.T) {
|
|||||||
s2 := sequences.Text()
|
s2 := sequences.Text()
|
||||||
s2 = s2[1:]
|
s2 = s2[1:]
|
||||||
|
|
||||||
x := wfa.WFAlign(s1, s2, testPenalties, false)
|
x := wfa.WFAlign(s1, s2, testPenalties, true)
|
||||||
gotScore := x.Score
|
gotScore := x.Score
|
||||||
|
|
||||||
if gotScore != -1*expectedScore {
|
if gotScore != -1*expectedScore {
|
||||||
|
Loading…
Reference in New Issue
Block a user