File size: 4,187 Bytes
b110593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//                           _       _
// __      _____  __ ___   ___  __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
//  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
//   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
//
//  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
//
//  CONTACT: [email protected]
//

package hybrid

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/go-openapi/strfmt"
	"github.com/stretchr/testify/assert"
	"github.com/weaviate/weaviate/entities/search"
)

func TestFusionRelativeScore(t *testing.T) {
	cases := []struct {
		weights        []float64
		inputScores    [][]float32
		expectedScores []float32
		expectedOrder  []uint64
	}{
		{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}},
		{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}},
		{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}},
		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{}, {}}, expectedScores: []float32{}, expectedOrder: []uint64{}},
		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{1}, {}}, expectedScores: []float32{0.75}, expectedOrder: []uint64{0}},
		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{}, {1}}, expectedScores: []float32{0.25}, expectedOrder: []uint64{0}},
		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{1, 2}, {}}, expectedScores: []float32{0.75, 0}, expectedOrder: []uint64{1, 0}},
		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{}, {1, 2}}, expectedScores: []float32{0.25, 0}, expectedOrder: []uint64{1, 0}},
		{weights: []float64{0.75, 0.25}, inputScores: [][]float32{{1, 1}, {1, 2}}, expectedScores: []float32{1, 0.75}, expectedOrder: []uint64{1, 0}},
		{weights: []float64{1}, inputScores: [][]float32{{1, 2, 3}}, expectedScores: []float32{1, 0.5, 0}, expectedOrder: []uint64{2, 1, 0}},
		{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}},
	}
	for _, tt := range cases {
		t.Run("hybrid fusion", func(t *testing.T) {
			var results [][]*Result
			for i := range tt.inputScores {
				var result []*Result
				for j, score := range tt.inputScores[i] {
					result = append(result, &Result{uint64(j), &search.Result{SecondarySortValue: score, ID: strfmt.UUID(fmt.Sprint(j))}})
				}
				results = append(results, result)
			}
			fused := FusionRelativeScore(tt.weights, results, []string{"set1", "set2"})
			fusedScores := []float32{} // don't use nil slice declaration, should be explicitly empty
			fusedOrder := []uint64{}

			for _, score := range fused {
				fusedScores = append(fusedScores, score.Score)
				fusedOrder = append(fusedOrder, score.DocID)
			}

			assert.InDeltaSlice(t, tt.expectedScores, fusedScores, 0.0001)
			assert.Equal(t, tt.expectedOrder, fusedOrder)
		})
	}
}

func TestFusionRelativeScoreExplain(t *testing.T) {
	result1 := []*Result{
		{uint64(1), &search.Result{SecondarySortValue: 0.5, ID: strfmt.UUID(fmt.Sprint(1)), ExplainScore: "keyword"}},
		{uint64(1), &search.Result{SecondarySortValue: 0.1, ID: strfmt.UUID(fmt.Sprint(2)), ExplainScore: "keyword"}},
	}
	result2 := []*Result{
		{uint64(1), &search.Result{SecondarySortValue: 2, ID: strfmt.UUID(fmt.Sprint(1)), ExplainScore: "vector"}},
		{uint64(1), &search.Result{SecondarySortValue: 1, ID: strfmt.UUID(fmt.Sprint(2)), ExplainScore: "vector"}},
	}
	results := [][]*Result{result1, result2}
	fused := FusionRelativeScore([]float64{0.5, 0.5}, results, []string{"keyword", "vector"})
	require.Contains(t, fused[0].ExplainScore, "(Result Set 'keyword') Document 1: original score 0.5, normalized score: 0.5")
	require.Contains(t, fused[0].ExplainScore, "(Result Set 'vector') Document 1: original score 2, normalized score: 0.5")
}