| // 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 graph_test |
| |
| import ( |
| "math" |
| "testing" |
| |
| "gonum.org/v1/gonum/graph" |
| "gonum.org/v1/gonum/graph/iterator" |
| "gonum.org/v1/gonum/graph/simple" |
| "gonum.org/v1/gonum/mat" |
| ) |
| |
| type weightedDirectedBuilder interface { |
| graph.WeightedBuilder |
| graph.WeightedDirected |
| } |
| |
| var weightedDirectedGraphs = []struct { |
| skipUnweighted bool |
| |
| g func() weightedDirectedBuilder |
| edges []simple.WeightedEdge |
| absent float64 |
| merge func(x, y float64, xe, ye graph.Edge) float64 |
| |
| want mat.Matrix |
| }{ |
| { |
| g: func() weightedDirectedBuilder { return simple.NewWeightedDirectedGraph(0, 0) }, |
| edges: []simple.WeightedEdge{ |
| {F: simple.Node(0), T: simple.Node(1), W: 2}, |
| {F: simple.Node(1), T: simple.Node(0), W: 1}, |
| {F: simple.Node(1), T: simple.Node(2), W: 1}, |
| }, |
| want: mat.NewSymDense(3, []float64{ |
| 0, (1. + 2.) / 2., 0, |
| (1. + 2.) / 2., 0, 1. / 2., |
| 0, 1. / 2., 0, |
| }), |
| }, |
| { |
| g: func() weightedDirectedBuilder { return simple.NewWeightedDirectedGraph(0, 0) }, |
| edges: []simple.WeightedEdge{ |
| {F: simple.Node(0), T: simple.Node(1), W: 2}, |
| {F: simple.Node(1), T: simple.Node(0), W: 1}, |
| {F: simple.Node(1), T: simple.Node(2), W: 1}, |
| }, |
| absent: 1, |
| merge: func(x, y float64, _, _ graph.Edge) float64 { return math.Sqrt(x * y) }, |
| want: mat.NewSymDense(3, []float64{ |
| 0, math.Sqrt(1 * 2), 0, |
| math.Sqrt(1 * 2), 0, math.Sqrt(1 * 1), |
| 0, math.Sqrt(1 * 1), 0, |
| }), |
| }, |
| { |
| skipUnweighted: true, // The min merge function cannot be used in the unweighted case. |
| |
| g: func() weightedDirectedBuilder { return simple.NewWeightedDirectedGraph(0, 0) }, |
| edges: []simple.WeightedEdge{ |
| {F: simple.Node(0), T: simple.Node(1), W: 2}, |
| {F: simple.Node(1), T: simple.Node(0), W: 1}, |
| {F: simple.Node(1), T: simple.Node(2), W: 1}, |
| }, |
| merge: func(x, y float64, _, _ graph.Edge) float64 { return math.Min(x, y) }, |
| want: mat.NewSymDense(3, []float64{ |
| 0, math.Min(1, 2), 0, |
| math.Min(1, 2), 0, math.Min(1, 0), |
| 0, math.Min(1, 0), 0, |
| }), |
| }, |
| { |
| g: func() weightedDirectedBuilder { return simple.NewWeightedDirectedGraph(0, 0) }, |
| edges: []simple.WeightedEdge{ |
| {F: simple.Node(0), T: simple.Node(1), W: 2}, |
| {F: simple.Node(1), T: simple.Node(0), W: 1}, |
| {F: simple.Node(1), T: simple.Node(2), W: 1}, |
| }, |
| merge: func(x, y float64, xe, ye graph.Edge) float64 { |
| if xe == nil { |
| return y |
| } |
| if ye == nil { |
| return x |
| } |
| return math.Min(x, y) |
| }, |
| want: mat.NewSymDense(3, []float64{ |
| 0, math.Min(1, 2), 0, |
| math.Min(1, 2), 0, 1, |
| 0, 1, 0, |
| }), |
| }, |
| { |
| g: func() weightedDirectedBuilder { return simple.NewWeightedDirectedGraph(0, 0) }, |
| edges: []simple.WeightedEdge{ |
| {F: simple.Node(0), T: simple.Node(1), W: 2}, |
| {F: simple.Node(1), T: simple.Node(0), W: 1}, |
| {F: simple.Node(1), T: simple.Node(2), W: 1}, |
| }, |
| merge: func(x, y float64, _, _ graph.Edge) float64 { return math.Max(x, y) }, |
| want: mat.NewSymDense(3, []float64{ |
| 0, math.Max(1, 2), 0, |
| math.Max(1, 2), 0, math.Max(1, 0), |
| 0, math.Max(1, 0), 0, |
| }), |
| }, |
| } |
| |
| func TestUndirect(t *testing.T) { |
| for i, test := range weightedDirectedGraphs { |
| if test.skipUnweighted { |
| continue |
| } |
| g := test.g() |
| for _, e := range test.edges { |
| g.SetWeightedEdge(e) |
| } |
| |
| src := graph.Undirect{G: g} |
| nodes := graph.NodesOf(src.Nodes()) |
| dst := simple.NewUndirectedMatrixFrom(nodes, 0, 0, 0) |
| for _, u := range nodes { |
| for _, v := range graph.NodesOf(src.From(u.ID())) { |
| dst.SetEdge(src.Edge(u.ID(), v.ID())) |
| } |
| } |
| |
| want := unit{test.want} |
| if !mat.Equal(dst.Matrix(), want) { |
| t.Errorf("unexpected result for case %d:\ngot:\n%.4v\nwant:\n%.4v", i, |
| mat.Formatted(dst.Matrix()), |
| mat.Formatted(want), |
| ) |
| } |
| } |
| } |
| |
| func TestUndirectWeighted(t *testing.T) { |
| for i, test := range weightedDirectedGraphs { |
| g := test.g() |
| for _, e := range test.edges { |
| g.SetWeightedEdge(e) |
| } |
| |
| src := graph.UndirectWeighted{G: g, Absent: test.absent, Merge: test.merge} |
| nodes := graph.NodesOf(src.Nodes()) |
| dst := simple.NewUndirectedMatrixFrom(nodes, 0, 0, 0) |
| for _, u := range nodes { |
| for _, v := range graph.NodesOf(src.From(u.ID())) { |
| dst.SetWeightedEdge(src.WeightedEdge(u.ID(), v.ID())) |
| } |
| } |
| |
| if !mat.Equal(dst.Matrix(), test.want) { |
| t.Errorf("unexpected result for case %d:\ngot:\n%.4v\nwant:\n%.4v", i, |
| mat.Formatted(dst.Matrix()), |
| mat.Formatted(test.want), |
| ) |
| } |
| } |
| } |
| |
| type unit struct { |
| mat.Matrix |
| } |
| |
| func (m unit) At(i, j int) float64 { |
| v := m.Matrix.At(i, j) |
| if v == 0 { |
| return 0 |
| } |
| return 1 |
| } |
| |
| var nodeIteratorPairTests = []struct { |
| a, b graph.Nodes |
| len int |
| }{ |
| {a: graph.Empty, b: graph.Empty, len: 0}, |
| {a: iterator.NewOrderedNodes(nil), b: iterator.NewOrderedNodes(nil), len: 0}, |
| {a: iterator.NewOrderedNodes([]graph.Node{simple.Node(0)}), b: graph.Empty, len: 1}, |
| {a: graph.Empty, b: iterator.NewOrderedNodes([]graph.Node{simple.Node(0)}), len: 1}, |
| {a: iterator.NewOrderedNodes([]graph.Node{simple.Node(0)}), b: iterator.NewOrderedNodes([]graph.Node{simple.Node(0)}), len: 1}, |
| {a: iterator.NewOrderedNodes([]graph.Node{simple.Node(0)}), b: iterator.NewOrderedNodes([]graph.Node{simple.Node(1)}), len: 2}, |
| {a: iterator.NewOrderedNodes([]graph.Node{simple.Node(0), simple.Node(1)}), b: iterator.NewOrderedNodes([]graph.Node{simple.Node(1)}), len: 2}, |
| {a: iterator.NewOrderedNodes([]graph.Node{simple.Node(1)}), b: iterator.NewOrderedNodes([]graph.Node{simple.Node(0), simple.Node(1)}), len: 2}, |
| } |
| |
| func TestNodeIteratorPair(t *testing.T) { |
| for _, test := range nodeIteratorPairTests { |
| it := graph.NewNodeIteratorPair(test.a, test.b) |
| for i := 0; i < 2; i++ { |
| n := it.Len() |
| if n != test.len { |
| t.Errorf("unexpected length of iterator construction/reset: got:%d want:%d", n, test.len) |
| } |
| for it.Next() { |
| n-- |
| } |
| if n != 0 { |
| t.Errorf("unexpected remaining nodes after iterator completion: got:%d want:0", n) |
| } |
| it.Reset() |
| } |
| } |
| } |