Files
obitools4/pkg/obidist/dist_matrix_test.go

387 lines
8.7 KiB
Go
Raw Permalink Normal View History

package obidist
import (
"math"
"testing"
)
func TestNewDistMatrix(t *testing.T) {
dm := NewDistMatrix(5)
if dm.Size() != 5 {
t.Errorf("Expected size 5, got %d", dm.Size())
}
// Check that all values are initialized to 0
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
if dm.Get(i, j) != 0.0 {
t.Errorf("Expected 0.0 at (%d, %d), got %f", i, j, dm.Get(i, j))
}
}
}
}
func TestDistMatrixDiagonal(t *testing.T) {
dm := NewDistMatrix(5)
// Diagonal should always be 0
for i := 0; i < 5; i++ {
if dm.Get(i, i) != 0.0 {
t.Errorf("Expected diagonal element (%d, %d) to be 0.0, got %f", i, i, dm.Get(i, i))
}
}
// Try to set diagonal (should be ignored)
dm.Set(2, 2, 5.0)
if dm.Get(2, 2) != 0.0 {
t.Errorf("Diagonal should remain 0.0 even after Set, got %f", dm.Get(2, 2))
}
}
func TestDistMatrixSymmetry(t *testing.T) {
dm := NewDistMatrix(4)
dm.Set(0, 1, 1.5)
dm.Set(0, 2, 2.5)
dm.Set(1, 3, 3.5)
// Check symmetry
if dm.Get(0, 1) != dm.Get(1, 0) {
t.Errorf("Matrix not symmetric: Get(0,1)=%f, Get(1,0)=%f", dm.Get(0, 1), dm.Get(1, 0))
}
if dm.Get(0, 2) != dm.Get(2, 0) {
t.Errorf("Matrix not symmetric: Get(0,2)=%f, Get(2,0)=%f", dm.Get(0, 2), dm.Get(2, 0))
}
if dm.Get(1, 3) != dm.Get(3, 1) {
t.Errorf("Matrix not symmetric: Get(1,3)=%f, Get(3,1)=%f", dm.Get(1, 3), dm.Get(3, 1))
}
}
func TestDistMatrixSetGet(t *testing.T) {
dm := NewDistMatrix(4)
testCases := []struct {
i int
j int
value float64
}{
{0, 1, 1.5},
{0, 2, 2.5},
{0, 3, 3.5},
{1, 2, 4.5},
{1, 3, 5.5},
{2, 3, 6.5},
}
for _, tc := range testCases {
dm.Set(tc.i, tc.j, tc.value)
}
for _, tc := range testCases {
got := dm.Get(tc.i, tc.j)
if math.Abs(got-tc.value) > 1e-10 {
t.Errorf("Get(%d, %d): expected %f, got %f", tc.i, tc.j, tc.value, got)
}
// Check symmetry
got = dm.Get(tc.j, tc.i)
if math.Abs(got-tc.value) > 1e-10 {
t.Errorf("Get(%d, %d) (symmetric): expected %f, got %f", tc.j, tc.i, tc.value, got)
}
}
}
func TestDistMatrixLabels(t *testing.T) {
labels := []string{"A", "B", "C", "D"}
dm := NewDistMatrixWithLabels(labels)
if dm.Size() != 4 {
t.Errorf("Expected size 4, got %d", dm.Size())
}
for i, label := range labels {
if dm.GetLabel(i) != label {
t.Errorf("Expected label %s at index %d, got %s", label, i, dm.GetLabel(i))
}
}
// Modify a label
dm.SetLabel(1, "Modified")
if dm.GetLabel(1) != "Modified" {
t.Errorf("Expected label 'Modified' at index 1, got %s", dm.GetLabel(1))
}
// Check Labels() returns a copy
labelsCopy := dm.Labels()
labelsCopy[0] = "ChangedCopy"
if dm.GetLabel(0) != "A" {
t.Errorf("Modifying Labels() return value should not affect original labels")
}
}
func TestDistMatrixMinDistance(t *testing.T) {
dm := NewDistMatrix(4)
dm.Set(0, 1, 2.5)
dm.Set(0, 2, 1.5) // minimum
dm.Set(0, 3, 3.5)
dm.Set(1, 2, 4.5)
dm.Set(1, 3, 5.5)
dm.Set(2, 3, 6.5)
minDist, minI, minJ := dm.MinDistance()
if math.Abs(minDist-1.5) > 1e-10 {
t.Errorf("Expected min distance 1.5, got %f", minDist)
}
if (minI != 0 || minJ != 2) && (minI != 2 || minJ != 0) {
t.Errorf("Expected min at (0, 2) or (2, 0), got (%d, %d)", minI, minJ)
}
}
func TestDistMatrixMaxDistance(t *testing.T) {
dm := NewDistMatrix(4)
dm.Set(0, 1, 2.5)
dm.Set(0, 2, 1.5)
dm.Set(0, 3, 3.5)
dm.Set(1, 2, 4.5)
dm.Set(1, 3, 5.5)
dm.Set(2, 3, 6.5) // maximum
maxDist, maxI, maxJ := dm.MaxDistance()
if math.Abs(maxDist-6.5) > 1e-10 {
t.Errorf("Expected max distance 6.5, got %f", maxDist)
}
if (maxI != 2 || maxJ != 3) && (maxI != 3 || maxJ != 2) {
t.Errorf("Expected max at (2, 3) or (3, 2), got (%d, %d)", maxI, maxJ)
}
}
func TestDistMatrixGetRow(t *testing.T) {
dm := NewDistMatrix(3)
dm.Set(0, 1, 1.0)
dm.Set(0, 2, 2.0)
dm.Set(1, 2, 3.0)
row0 := dm.GetRow(0)
expected0 := []float64{0.0, 1.0, 2.0}
for i, val := range expected0 {
if math.Abs(row0[i]-val) > 1e-10 {
t.Errorf("Row 0[%d]: expected %f, got %f", i, val, row0[i])
}
}
row1 := dm.GetRow(1)
expected1 := []float64{1.0, 0.0, 3.0}
for i, val := range expected1 {
if math.Abs(row1[i]-val) > 1e-10 {
t.Errorf("Row 1[%d]: expected %f, got %f", i, val, row1[i])
}
}
}
func TestDistMatrixCopy(t *testing.T) {
dm := NewDistMatrixWithLabels([]string{"A", "B", "C"})
dm.Set(0, 1, 1.5)
dm.Set(0, 2, 2.5)
dm.Set(1, 2, 3.5)
dmCopy := dm.Copy()
// Check values are copied
if dmCopy.Get(0, 1) != dm.Get(0, 1) {
t.Errorf("Copy has different value at (0, 1)")
}
// Check labels are copied
if dmCopy.GetLabel(0) != dm.GetLabel(0) {
t.Errorf("Copy has different label at index 0")
}
// Modify copy and ensure original unchanged
dmCopy.Set(0, 1, 99.9)
if dm.Get(0, 1) == 99.9 {
t.Errorf("Modifying copy affected original matrix")
}
dmCopy.SetLabel(0, "Modified")
if dm.GetLabel(0) == "Modified" {
t.Errorf("Modifying copy label affected original matrix")
}
}
func TestDistMatrixToFullMatrix(t *testing.T) {
dm := NewDistMatrix(3)
dm.Set(0, 1, 1.0)
dm.Set(0, 2, 2.0)
dm.Set(1, 2, 3.0)
full := dm.ToFullMatrix()
expected := [][]float64{
{0.0, 1.0, 2.0},
{1.0, 0.0, 3.0},
{2.0, 3.0, 0.0},
}
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if math.Abs(full[i][j]-expected[i][j]) > 1e-10 {
t.Errorf("Full matrix[%d][%d]: expected %f, got %f",
i, j, expected[i][j], full[i][j])
}
}
}
}
func TestDistMatrixBoundsChecking(t *testing.T) {
dm := NewDistMatrix(3)
// Test Get out of bounds
testPanic := func(f func()) {
defer func() {
if r := recover(); r == nil {
t.Errorf("Expected panic, but didn't get one")
}
}()
f()
}
testPanic(func() { dm.Get(-1, 0) })
testPanic(func() { dm.Get(0, 3) })
testPanic(func() { dm.Set(3, 0, 1.0) })
testPanic(func() { dm.GetLabel(-1) })
testPanic(func() { dm.SetLabel(3, "Invalid") })
testPanic(func() { dm.GetRow(3) })
}
func TestDistMatrixEmptyMatrix(t *testing.T) {
dm := NewDistMatrix(0)
if dm.Size() != 0 {
t.Errorf("Expected size 0, got %d", dm.Size())
}
minDist, minI, minJ := dm.MinDistance()
if minDist != 0.0 || minI != -1 || minJ != -1 {
t.Errorf("Empty matrix MinDistance should return (0.0, -1, -1), got (%f, %d, %d)",
minDist, minI, minJ)
}
maxDist, maxI, maxJ := dm.MaxDistance()
if maxDist != 0.0 || maxI != -1 || maxJ != -1 {
t.Errorf("Empty matrix MaxDistance should return (0.0, -1, -1), got (%f, %d, %d)",
maxDist, maxI, maxJ)
}
}
func TestDistMatrixSingleElement(t *testing.T) {
dm := NewDistMatrix(1)
if dm.Size() != 1 {
t.Errorf("Expected size 1, got %d", dm.Size())
}
// Only element is diagonal (always 0)
if dm.Get(0, 0) != 0.0 {
t.Errorf("Expected 0.0 at (0, 0), got %f", dm.Get(0, 0))
}
minDist, minI, minJ := dm.MinDistance()
if minDist != 0.0 || minI != -1 || minJ != -1 {
t.Errorf("Single element matrix MinDistance should return (0.0, -1, -1), got (%f, %d, %d)",
minDist, minI, minJ)
}
}
func TestNewSimilarityMatrix(t *testing.T) {
sm := NewSimilarityMatrix(4)
if sm.Size() != 4 {
t.Errorf("Expected size 4, got %d", sm.Size())
}
// Check diagonal is 1.0
for i := 0; i < 4; i++ {
if sm.Get(i, i) != 1.0 {
t.Errorf("Expected diagonal element (%d, %d) to be 1.0, got %f", i, i, sm.Get(i, i))
}
}
// Check off-diagonal is 0.0
if sm.Get(0, 1) != 0.0 {
t.Errorf("Expected off-diagonal to be 0.0, got %f", sm.Get(0, 1))
}
}
func TestNewSimilarityMatrixWithLabels(t *testing.T) {
labels := []string{"A", "B", "C"}
sm := NewSimilarityMatrixWithLabels(labels)
if sm.Size() != 3 {
t.Errorf("Expected size 3, got %d", sm.Size())
}
// Check labels
for i, label := range labels {
if sm.GetLabel(i) != label {
t.Errorf("Expected label %s at index %d, got %s", label, i, sm.GetLabel(i))
}
}
// Check diagonal is 1.0
for i := 0; i < 3; i++ {
if sm.Get(i, i) != 1.0 {
t.Errorf("Expected diagonal element (%d, %d) to be 1.0, got %f", i, i, sm.Get(i, i))
}
}
// Set some similarities
sm.Set(0, 1, 0.8)
sm.Set(0, 2, 0.6)
sm.Set(1, 2, 0.7)
// Check values
if math.Abs(sm.Get(0, 1)-0.8) > 1e-10 {
t.Errorf("Expected 0.8 at (0, 1), got %f", sm.Get(0, 1))
}
if math.Abs(sm.Get(1, 0)-0.8) > 1e-10 {
t.Errorf("Expected 0.8 at (1, 0) (symmetry), got %f", sm.Get(1, 0))
}
}
func TestSimilarityMatrixCopy(t *testing.T) {
sm := NewSimilarityMatrix(3)
sm.Set(0, 1, 0.9)
sm.Set(0, 2, 0.7)
smCopy := sm.Copy()
// Check diagonal is preserved
if smCopy.Get(0, 0) != 1.0 {
t.Errorf("Copied similarity matrix should have diagonal 1.0, got %f", smCopy.Get(0, 0))
}
// Check values are preserved
if math.Abs(smCopy.Get(0, 1)-0.9) > 1e-10 {
t.Errorf("Copy should preserve values, expected 0.9, got %f", smCopy.Get(0, 1))
}
// Modify copy and ensure original unchanged
smCopy.Set(0, 1, 0.5)
if math.Abs(sm.Get(0, 1)-0.9) > 1e-10 {
t.Errorf("Modifying copy should not affect original, expected 0.9, got %f", sm.Get(0, 1))
}
}