// 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/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} dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0) for _, u := range src.Nodes() { for _, v := range src.From(u) { dst.SetEdge(src.Edge(u, v)) } } 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} dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0) for _, u := range src.Nodes() { for _, v := range src.From(u) { dst.SetWeightedEdge(src.WeightedEdge(u, v)) } } 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 }