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  }