mirror of
https://github.com/gonum/gonum.git
synced 2025-10-06 15:47:01 +08:00

Changes made in dsp/fourier/internal/fftpack break the formatting used there, so these are reverted. There will be complaints in CI. [git-generate] gofmt -w . go generate gonum.org/v1/gonum/blas go generate gonum.org/v1/gonum/blas/gonum go generate gonum.org/v1/gonum/unit go generate gonum.org/v1/gonum/unit/constant go generate gonum.org/v1/gonum/graph/formats/dot go generate gonum.org/v1/gonum/graph/formats/rdf go generate gonum.org/v1/gonum/stat/card git checkout -- dsp/fourier/internal/fftpack
99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
// Copyright ©2017 The Gonum Authors. All rights reserved.
|
||
// Use of this source code is governed by a BSD-style
|
||
// license that can be found in the LICENSE file.
|
||
|
||
package network
|
||
|
||
import (
|
||
"gonum.org/v1/gonum/graph/spectral"
|
||
"gonum.org/v1/gonum/mat"
|
||
)
|
||
|
||
// Diffuse performs a heat diffusion across nodes of the undirected
|
||
// graph described by the given Laplacian using the initial heat distribution,
|
||
// h, according to the Laplacian with a diffusion time of t.
|
||
// The resulting heat distribution is returned, written into the map dst and
|
||
// returned,
|
||
//
|
||
// d = exp(-Lt)×h
|
||
//
|
||
// where L is the graph Laplacian. Indexing into h and dst is defined by the
|
||
// Laplacian Index field. If dst is nil, a new map is created.
|
||
//
|
||
// Nodes without corresponding entries in h are given an initial heat of zero,
|
||
// and entries in h without a corresponding node in the original graph are
|
||
// not altered when written to dst.
|
||
func Diffuse(dst, h map[int64]float64, by spectral.Laplacian, t float64) map[int64]float64 {
|
||
heat := make([]float64, len(by.Index))
|
||
for id, i := range by.Index {
|
||
heat[i] = h[id]
|
||
}
|
||
v := mat.NewVecDense(len(heat), heat)
|
||
|
||
var m, tl mat.Dense
|
||
tl.Scale(-t, by)
|
||
m.Exp(&tl)
|
||
v.MulVec(&m, v)
|
||
|
||
if dst == nil {
|
||
dst = make(map[int64]float64)
|
||
}
|
||
for i, n := range heat {
|
||
dst[by.Nodes[i].ID()] = n
|
||
}
|
||
return dst
|
||
}
|
||
|
||
// DiffuseToEquilibrium performs a heat diffusion across nodes of the
|
||
// graph described by the given Laplacian using the initial heat
|
||
// distribution, h, according to the Laplacian until the update function
|
||
//
|
||
// h_{n+1} = h_n - L×h_n
|
||
//
|
||
// results in a 2-norm update difference within tol, or iters updates have
|
||
// been made.
|
||
// The resulting heat distribution is returned as eq, written into the map dst,
|
||
// and a boolean indicating whether the equilibrium converged to within tol.
|
||
// Indexing into h and dst is defined by the Laplacian Index field. If dst
|
||
// is nil, a new map is created.
|
||
//
|
||
// Nodes without corresponding entries in h are given an initial heat of zero,
|
||
// and entries in h without a corresponding node in the original graph are
|
||
// not altered when written to dst.
|
||
func DiffuseToEquilibrium(dst, h map[int64]float64, by spectral.Laplacian, tol float64, iters int) (eq map[int64]float64, ok bool) {
|
||
heat := make([]float64, len(by.Index))
|
||
for id, i := range by.Index {
|
||
heat[i] = h[id]
|
||
}
|
||
v := mat.NewVecDense(len(heat), heat)
|
||
|
||
last := make([]float64, len(by.Index))
|
||
for id, i := range by.Index {
|
||
last[i] = h[id]
|
||
}
|
||
lastV := mat.NewVecDense(len(last), last)
|
||
|
||
var tmp mat.VecDense
|
||
for {
|
||
iters--
|
||
if iters < 0 {
|
||
break
|
||
}
|
||
lastV, v = v, lastV
|
||
tmp.MulVec(by.Matrix, lastV)
|
||
v.SubVec(lastV, &tmp)
|
||
if normDiff(heat, last) < tol {
|
||
ok = true
|
||
break
|
||
}
|
||
}
|
||
|
||
if dst == nil {
|
||
dst = make(map[int64]float64)
|
||
}
|
||
for i, n := range v.RawVector().Data {
|
||
dst[by.Nodes[i].ID()] = n
|
||
}
|
||
return dst, ok
|
||
}
|