| // Copyright ©2015 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 gen |
| |
| import ( |
| "fmt" |
| "math" |
| "math/rand" |
| "sort" |
| |
| "gonum.org/v1/gonum/graph" |
| "gonum.org/v1/gonum/graph/internal/ordered" |
| "gonum.org/v1/gonum/graph/simple" |
| ) |
| |
| // UndirectedMutator is an undirected graph builder that can remove edges. |
| type UndirectedMutator interface { |
| graph.UndirectedBuilder |
| graph.EdgeRemover |
| } |
| |
| // Duplication constructs a graph in the destination, dst, of order n. New nodes |
| // are created by duplicating an existing node and all its edges. Each new edge is |
| // deleted with probability delta. Additional edges are added between the new node |
| // and existing nodes with probability alpha/|V|. An exception to this addition |
| // rule is made for the parent node when sigma is not NaN; in this case an edge is |
| // created with probability sigma. With the exception of the sigma parameter, this |
| // corresponds to the completely correlated case in doi:10.1016/S0022-5193(03)00028-6. |
| // If src is not nil it is used as the random source, otherwise rand.Float64 is used. |
| func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src *rand.Rand) error { |
| // As described in doi:10.1016/S0022-5193(03)00028-6 but |
| // also clarified in doi:10.1186/gb-2007-8-4-r51. |
| |
| if delta < 0 || delta > 1 { |
| return fmt.Errorf("gen: bad delta: delta=%v", delta) |
| } |
| if alpha <= 0 || alpha > 1 { |
| return fmt.Errorf("gen: bad alpha: alpha=%v", alpha) |
| } |
| if sigma < 0 || sigma > 1 { |
| return fmt.Errorf("gen: bad sigma: sigma=%v", sigma) |
| } |
| |
| var ( |
| rnd func() float64 |
| rndN func(int) int |
| ) |
| if src == nil { |
| rnd = rand.Float64 |
| rndN = rand.Intn |
| } else { |
| rnd = src.Float64 |
| rndN = src.Intn |
| } |
| |
| nodes := dst.Nodes() |
| sort.Sort(ordered.ByID(nodes)) |
| if len(nodes) == 0 { |
| n-- |
| u := dst.NewNode() |
| dst.AddNode(u) |
| nodes = append(nodes, u) |
| } |
| for i := 0; i < n; i++ { |
| u := nodes[rndN(len(nodes))] |
| d := dst.NewNode() |
| |
| // Add the duplicate node. |
| dst.AddNode(d) |
| |
| // Loop until we have connectivity |
| // into the rest of the graph. |
| for { |
| // Add edges to parent's neighbours. |
| to := dst.From(u) |
| sort.Sort(ordered.ByID(to)) |
| for _, v := range to { |
| if rnd() < delta || dst.HasEdgeBetween(v, d) { |
| continue |
| } |
| if v.ID() < d.ID() { |
| dst.SetEdge(simple.Edge{F: v, T: d, W: 1}) |
| } else { |
| dst.SetEdge(simple.Edge{F: d, T: v, W: 1}) |
| } |
| } |
| |
| // Add edges to old nodes. |
| scaledAlpha := alpha / float64(len(nodes)) |
| for _, v := range nodes { |
| switch v.ID() { |
| case u.ID(): |
| if !math.IsNaN(sigma) { |
| if i == 0 || rnd() < sigma { |
| if v.ID() < d.ID() { |
| dst.SetEdge(simple.Edge{F: v, T: d, W: 1}) |
| } else { |
| dst.SetEdge(simple.Edge{F: d, T: v, W: 1}) |
| } |
| } |
| continue |
| } |
| fallthrough |
| default: |
| if rnd() < scaledAlpha && !dst.HasEdgeBetween(v, d) { |
| if v.ID() < d.ID() { |
| dst.SetEdge(simple.Edge{F: v, T: d, W: 1}) |
| } else { |
| dst.SetEdge(simple.Edge{F: d, T: v, W: 1}) |
| } |
| } |
| } |
| } |
| |
| if len(dst.From(d)) != 0 { |
| break |
| } |
| } |
| |
| nodes = append(nodes, d) |
| } |
| |
| return nil |
| } |