github.com/gopherd/gonum@v0.0.4/graph/layout/isomap.go (about) 1 // Copyright ©2019 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package layout 6 7 import ( 8 "github.com/gopherd/gonum/graph" 9 "github.com/gopherd/gonum/graph/path" 10 "github.com/gopherd/gonum/mat" 11 "github.com/gopherd/gonum/spatial/r2" 12 "github.com/gopherd/gonum/stat/mds" 13 ) 14 15 // IsomapR2 implements a graph layout algorithm based on the Isomap 16 // non-linear dimensionality reduction method. Coordinates of nodes 17 // are computed by finding a Torgerson multidimensional scaling of 18 // the shortest path distances between all pairs of node in the graph. 19 // The all pair shortest path distances are calculated using the 20 // Floyd-Warshall algorithm and so IsomapR2 will not scale to large 21 // graphs. Graphs with more than one connected component cannot be 22 // laid out by IsomapR2. 23 type IsomapR2 struct{} 24 25 // Update is the IsomapR2 spatial graph update function. 26 func (IsomapR2) Update(g graph.Graph, layout LayoutR2) bool { 27 nodes := graph.NodesOf(g.Nodes()) 28 v := isomap(g, nodes, 2) 29 if v == nil { 30 return false 31 } 32 33 // FIXME(kortschak): The Layout types do not have the capacity to 34 // be cleared in the current API. Is this a problem? I don't know 35 // at this stage. It might be if the layout is reused between graphs. 36 // Someone may do this. 37 38 for i, n := range nodes { 39 layout.SetCoord2(n.ID(), r2.Vec{X: v.At(i, 0), Y: v.At(i, 1)}) 40 } 41 return false 42 } 43 44 func isomap(g graph.Graph, nodes []graph.Node, dims int) *mat.Dense { 45 p, ok := path.FloydWarshall(g) 46 if !ok { 47 return nil 48 } 49 50 dist := mat.NewSymDense(len(nodes), nil) 51 for i, u := range nodes { 52 for j, v := range nodes { 53 dist.SetSym(i, j, p.Weight(u.ID(), v.ID())) 54 } 55 } 56 var v mat.Dense 57 k, _ := mds.TorgersonScaling(&v, nil, dist) 58 if k < dims { 59 return nil 60 } 61 62 return &v 63 }