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  }