github.com/weaviate/weaviate@v1.24.6/usecases/traverser/hybrid/fusion_test.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package hybrid
    13  
    14  import (
    15  	"fmt"
    16  	"testing"
    17  
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/go-openapi/strfmt"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/weaviate/weaviate/entities/search"
    23  )
    24  
    25  func TestFusionRelativeScore(t *testing.T) {
    26  	cases := []struct {
    27  		weights        []float64
    28  		inputScores    [][]float32
    29  		expectedScores []float32
    30  		expectedOrder  []uint64
    31  	}{
    32  		{weights: []float64{0.5, 0.5}, inputScores: [][]float32{{1, 2, 3}, {0, 1, 2}}, expectedScores: []float32{1, 0.5, 0}, expectedOrder: []uint64{2, 1, 0}},
    33  		{weights: []float64{0.5, 0.5}, inputScores: [][]float32{{0, 2, 0.1}, {0, 0.2, 2}}, expectedScores: []float32{0.55, 0.525, 0}, expectedOrder: []uint64{1, 2, 0}},
    34  		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{0.5, 0.5, 0}, {0, 0.01, 0.001}}, expectedScores: []float32{1, 0.75, 0.025}, expectedOrder: []uint64{1, 0, 2}},
    35  		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{}, {}}, expectedScores: []float32{}, expectedOrder: []uint64{}},
    36  		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{1}, {}}, expectedScores: []float32{0.75}, expectedOrder: []uint64{0}},
    37  		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{}, {1}}, expectedScores: []float32{0.25}, expectedOrder: []uint64{0}},
    38  		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{1, 2}, {}}, expectedScores: []float32{0.75, 0}, expectedOrder: []uint64{1, 0}},
    39  		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{}, {1, 2}}, expectedScores: []float32{0.25, 0}, expectedOrder: []uint64{1, 0}},
    40  		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{1, 1}, {1, 2}}, expectedScores: []float32{1, 0.75}, expectedOrder: []uint64{1, 0}},
    41  		{weights: []float64{1}, inputScores: [][]float32{{1, 2, 3}}, expectedScores: []float32{1, 0.5, 0}, expectedOrder: []uint64{2, 1, 0}},
    42  		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{1, 2, 3, 4}, {1, 2, 3}}, expectedScores: []float32{0.75, 0.75, 0.375, 0}, expectedOrder: []uint64{3, 2, 1, 0}},
    43  	}
    44  	for _, tt := range cases {
    45  		t.Run("hybrid fusion", func(t *testing.T) {
    46  			var results [][]*search.Result
    47  			for i := range tt.inputScores {
    48  				var result []*search.Result
    49  				for j, score := range tt.inputScores[i] {
    50  					docId := uint64(j)
    51  					result = append(result, &search.Result{SecondarySortValue: score, DocID: &docId, ID: strfmt.UUID(fmt.Sprint(j))})
    52  				}
    53  				results = append(results, result)
    54  			}
    55  			fused := FusionRelativeScore(tt.weights, results, []string{"set1", "set2"})
    56  			fusedScores := []float32{} // don't use nil slice declaration, should be explicitly empty
    57  			fusedOrder := []uint64{}
    58  
    59  			for _, score := range fused {
    60  				fusedScores = append(fusedScores, score.Score)
    61  				fusedOrder = append(fusedOrder, *score.DocID)
    62  			}
    63  
    64  			assert.InDeltaSlice(t, tt.expectedScores, fusedScores, 0.0001)
    65  			assert.Equal(t, tt.expectedOrder, fusedOrder)
    66  		})
    67  	}
    68  }
    69  
    70  func TestFusionRelativeScoreExplain(t *testing.T) {
    71  	docId1 := uint64(1)
    72  	docId2 := uint64(2)
    73  	result1 := []*search.Result{
    74  		{DocID: &docId1, SecondarySortValue: 0.5, ID: strfmt.UUID(fmt.Sprint(1))},
    75  		{DocID: &docId2, SecondarySortValue: 0.1, ID: strfmt.UUID(fmt.Sprint(2))},
    76  	}
    77  
    78  	result2 := []*search.Result{
    79  		{DocID: &docId1, SecondarySortValue: 2, ID: strfmt.UUID(fmt.Sprint(1))},
    80  		{DocID: &docId2, SecondarySortValue: 1, ID: strfmt.UUID(fmt.Sprint(2))},
    81  	}
    82  	results := [][]*search.Result{result1, result2}
    83  	fused := FusionRelativeScore([]float64{0.5, 0.5}, results, []string{"keyword", "vector"})
    84  	require.Contains(t, fused[0].ExplainScore, "(Result Set keyword) Document 1: original score 0.5, normalized score: 0.5")
    85  	require.Contains(t, fused[0].ExplainScore, "(Result Set vector) Document 1: original score 2, normalized score: 0.5")
    86  }