diff --git a/go.mod b/go.mod index 559c98b..523bfaf 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/stretchr/testify v1.8.2 github.com/urfave/cli/v2 v2.25.1 github.com/vmihailenco/msgpack/v5 v5.3.5 + gonum.org/v1/gonum v0.14.0 google.golang.org/protobuf v1.30.0 ) diff --git a/go.sum b/go.sum index ac9e599..4e4b925 100644 --- a/go.sum +++ b/go.sum @@ -502,6 +502,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/matrix/api.go b/matrix/api.go new file mode 100644 index 0000000..9e5f346 --- /dev/null +++ b/matrix/api.go @@ -0,0 +1,100 @@ +package matrix + +import ( + "github.com/dominikbraun/graph" + "github.com/williamfzc/srctx/graph/common" + "gonum.org/v1/gonum/mat" +) + +const ( + InvalidIndex = -1 + InvalidValue = -1.0 +) + +/* +Matrix + +Standard relationship representation layer based on matrix +*/ +type Matrix struct { + IndexMap map[string]int + ReverseIndexMap map[int]string + Data *mat.Dense +} + +func (m *Matrix) Size() int { + return len(m.IndexMap) +} + +func (m *Matrix) Id(s string) int { + if item, ok := m.IndexMap[s]; ok { + return item + } + return InvalidIndex +} + +func (m *Matrix) ById(id int) string { + if item, ok := m.ReverseIndexMap[id]; ok { + return item + } + return "" +} + +func (m *Matrix) ForEach(s string, f func(i int, v float64)) { + index := m.Id(s) + if index == InvalidIndex { + return + } + + for i := 0; i < m.Size(); i++ { + f(i, m.Data.At(i, index)) + } +} + +func CreateMatrixFromGraph[T string, U any](g graph.Graph[T, U]) (*Matrix, error) { + adjacencyMap, err := g.AdjacencyMap() + if err != nil { + return nil, err + } + + nodeCount := len(adjacencyMap) + data := mat.NewDense(nodeCount, nodeCount, nil) + + indexMap := make(map[string]int) + i := 0 + for node := range adjacencyMap { + indexMap[string(node)] = i + i++ + } + + for source, edges := range adjacencyMap { + sourceIndex := indexMap[string(source)] + + data.Set(sourceIndex, sourceIndex, float64(len(edges))) + + for target, edge := range edges { + storage := edge.Properties.Data.(*common.EdgeStorage) + targetIndex := indexMap[string(target)] + + currentValue := data.At(targetIndex, sourceIndex) + data.Set(targetIndex, sourceIndex, currentValue+float64(len(storage.RefLines))) + } + } + + ret := &Matrix{ + IndexMap: indexMap, + ReverseIndexMap: reverseMap(indexMap), + Data: data, + } + return ret, nil +} + +func reverseMap(originalMap map[string]int) map[int]string { + reversedMap := make(map[int]string) + + for key, value := range originalMap { + reversedMap[value] = key + } + + return reversedMap +} diff --git a/matrix/api_test.go b/matrix/api_test.go new file mode 100644 index 0000000..3edee04 --- /dev/null +++ b/matrix/api_test.go @@ -0,0 +1,33 @@ +package matrix + +import ( + "path/filepath" + "runtime" + "testing" + + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/williamfzc/srctx/graph/common" + "github.com/williamfzc/srctx/graph/file" +) + +func TestMatrix(t *testing.T) { + _, curFile, _, _ := runtime.Caller(0) + src := filepath.Dir(filepath.Dir(curFile)) + opts := common.DefaultGraphOptions() + opts.Src = src + opts.LsifFile = filepath.Join(src, "dump.lsif") + fileGraph, err := file.CreateFileGraphFromDirWithLSIF(opts) + assert.Nil(t, err) + + t.Run("test_a", func(t *testing.T) { + matrixFromGraph, err := CreateMatrixFromGraph(fileGraph.G) + assert.Nil(t, err) + assert.NotEmpty(t, matrixFromGraph) + + targetFile := "graph/common/edge.go" + matrixFromGraph.ForEach(targetFile, func(i int, v float64) { + log.Infof("%s value: %v", matrixFromGraph.ById(i), v) + }) + }) +}