github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/network/diffusion.go (about) 1 // Copyright ©2017 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 network 6 7 import ( 8 "github.com/jingcheng-WU/gonum/graph/spectral" 9 "github.com/jingcheng-WU/gonum/mat" 10 ) 11 12 // Diffuse performs a heat diffusion across nodes of the undirected 13 // graph described by the given Laplacian using the initial heat distribution, 14 // h, according to the Laplacian with a diffusion time of t. 15 // The resulting heat distribution is returned, written into the map dst and 16 // returned, 17 // d = exp(-Lt)×h 18 // where L is the graph Laplacian. Indexing into h and dst is defined by the 19 // Laplacian Index field. If dst is nil, a new map is created. 20 // 21 // Nodes without corresponding entries in h are given an initial heat of zero, 22 // and entries in h without a corresponding node in the original graph are 23 // not altered when written to dst. 24 func Diffuse(dst, h map[int64]float64, by spectral.Laplacian, t float64) map[int64]float64 { 25 heat := make([]float64, len(by.Index)) 26 for id, i := range by.Index { 27 heat[i] = h[id] 28 } 29 v := mat.NewVecDense(len(heat), heat) 30 31 var m, tl mat.Dense 32 tl.Scale(-t, by) 33 m.Exp(&tl) 34 v.MulVec(&m, v) 35 36 if dst == nil { 37 dst = make(map[int64]float64) 38 } 39 for i, n := range heat { 40 dst[by.Nodes[i].ID()] = n 41 } 42 return dst 43 } 44 45 // DiffuseToEquilibrium performs a heat diffusion across nodes of the 46 // graph described by the given Laplacian using the initial heat 47 // distribution, h, according to the Laplacian until the update function 48 // h_{n+1} = h_n - L×h_n 49 // results in a 2-norm update difference within tol, or iters updates have 50 // been made. 51 // The resulting heat distribution is returned as eq, written into the map dst, 52 // and a boolean indicating whether the equilibrium converged to within tol. 53 // Indexing into h and dst is defined by the Laplacian Index field. If dst 54 // is nil, a new map is created. 55 // 56 // Nodes without corresponding entries in h are given an initial heat of zero, 57 // and entries in h without a corresponding node in the original graph are 58 // not altered when written to dst. 59 func DiffuseToEquilibrium(dst, h map[int64]float64, by spectral.Laplacian, tol float64, iters int) (eq map[int64]float64, ok bool) { 60 heat := make([]float64, len(by.Index)) 61 for id, i := range by.Index { 62 heat[i] = h[id] 63 } 64 v := mat.NewVecDense(len(heat), heat) 65 66 last := make([]float64, len(by.Index)) 67 for id, i := range by.Index { 68 last[i] = h[id] 69 } 70 lastV := mat.NewVecDense(len(last), last) 71 72 var tmp mat.VecDense 73 for { 74 iters-- 75 if iters < 0 { 76 break 77 } 78 lastV, v = v, lastV 79 tmp.MulVec(by.Matrix, lastV) 80 v.SubVec(lastV, &tmp) 81 if normDiff(heat, last) < tol { 82 ok = true 83 break 84 } 85 } 86 87 if dst == nil { 88 dst = make(map[int64]float64) 89 } 90 for i, n := range v.RawVector().Data { 91 dst[by.Nodes[i].ID()] = n 92 } 93 return dst, ok 94 }