diff --git a/metrics/pairwise/cosine.go b/metrics/pairwise/cosine.go new file mode 100644 index 0000000..de0503f --- /dev/null +++ b/metrics/pairwise/cosine.go @@ -0,0 +1,34 @@ +package pairwise + +import ( + "math" + + "github.com/gonum/matrix/mat64" +) + +type Cosine struct{} + +func NewCosine() *Cosine { + return &Cosine{} +} + +// Dot computes dot value of vectorX and vectorY. +func (c *Cosine) Dot(vectorX *mat64.Dense, vectorY *mat64.Dense) float64 { + subVector := mat64.NewDense(0, 0, nil) + subVector.MulElem(vectorX, vectorY) + result := mat64.Sum(subVector) + + return result +} + +// Distance computes Cosine distance. +// It will return distance which represented as 1-cos() (ranged from 0 to 2). +func (c *Cosine) Distance(vectorX *mat64.Dense, vectorY *mat64.Dense) float64 { + dotXY := c.Dot(vectorX, vectorY) + lengthX := math.Sqrt(c.Dot(vectorX, vectorX)) + lengthY := math.Sqrt(c.Dot(vectorY, vectorY)) + + cos := dotXY / (lengthX*lengthY) + + return 1-cos +} diff --git a/metrics/pairwise/cosine_test.go b/metrics/pairwise/cosine_test.go new file mode 100644 index 0000000..2a3078e --- /dev/null +++ b/metrics/pairwise/cosine_test.go @@ -0,0 +1,36 @@ +package pairwise + +import ( + "testing" + + "github.com/gonum/matrix/mat64" + . "github.com/smartystreets/goconvey/convey" +) + +func TestCosine(t *testing.T) { + var vectorX, vectorY *mat64.Dense + cosine := NewCosine() + + Convey("Given two vectors", t, func() { + vectorX = mat64.NewDense(3, 1, []float64{1, 2, 3}) + vectorY = mat64.NewDense(3, 1, []float64{2, 4, 6}) + + Convey("When doing inner Dot", func() { + result := cosine.Dot(vectorX, vectorY) + + Convey("The result should be 25", func() { + So(result, ShouldEqual, 28) + }) + }) + + Convey("When calculating distance", func() { + result := cosine.Distance(vectorX, vectorY) + + Convey("The result should be 0", func() { + So(result, ShouldEqual, 0) + }) + + }) + + }) +}