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