func TestSettingZeroDistanceAndExact(t *testing.T) { expected := [][]bool{ []bool{false, true, false, false, false, false, false}, []bool{false, true, true, false, false, false, false}, []bool{false, false, true, false, false, true, false}, []bool{false, false, false, false, false, false, false}, []bool{false, false, false, false, false, false, false}, []bool{false, false, false, false, false, false, false}, []bool{false, false, false, false, false, false, false}} c, m, visited, exact, d := coupling.SetUpTest() w := matching.FindFeasibleMatching(m, 0, 3, &c) setpair.Setpair(m, w, exact, visited, d, &c) nonz := findNonZero(w, exact, d, &c) setZerosDistanceToZero(w, nonz, exact, d, &c) for i := 0; i < len(expected); i++ { for j := 0; j < len(expected[0]); j++ { assert.Equal(t, expected[i][j], exact[i][j], "the cell (%v,%v) were not correcly set", i, j) } } assert.True(t, utils.ApproxEqual(d[0][1], 1), "the distance for node (0,1) was changed") assert.True(t, utils.ApproxEqual(d[2][2], 0), "the distance for node (1,1) was not set to 0") }
func TestGuassian(t *testing.T) { a := [][]float64{[]float64{1.0, -(1.0 / 6.0)}, []float64{0.0, 1.0}} b := []float64{5.0 / 6.0, 1.0 / 2.0} x, err := GaussPartial(a, b) assert.Equal(t, nil, err, "the linear equations was not calculate correctly") assert.True(t, utils.ApproxEqual(x[0], 11.0/12.0), "the found x value was not 11/12") assert.True(t, utils.ApproxEqual(x[1], 0.5), "the found x value was not 1/2") }
func TestDisc(t *testing.T) { c, m, visited, exact, d := coupling.SetUpTest() w := matching.FindFeasibleMatching(m, 0, 3, &c) setpair.Setpair(m, w, exact, visited, d, &c) Disc(1.0, w, exact, d, &c) assert.True(t, utils.ApproxEqual(d[0][3], 0.9133), "the distance was not correctly set") assert.True(t, utils.ApproxEqual(d[2][3], 0.49), "the distance was not correctly set") }
func TestGoVerticalReturnsTrue(t *testing.T) { c := setUpCoupling() min := 0.3 n := c.Nodes[1] done := goVertical(n, 1, 0, 1, 1, 2, 2, 2, &min) assert.True(t, done, "goVertical did not compplete the stepping stone path") assert.True(t, utils.ApproxEqual(0.0, n.Adj[1][0].Prob), "the cell (1 0) were changed") assert.False(t, utils.ApproxEqual(0.3, n.Adj[1][1].Prob), "the cell (1 1) were not changed") }
func updateEdge(edge *coupling.Edge, signal bool, min float64, n *coupling.Node) { if signal { log.Printf("increasing at cell (%v,%v)", edge.To.S, edge.To.T) // increase and set the node to basic edge.Prob += min if !edge.Basic { edge.Basic = true n.BasicCount++ } // add the main node as a successor to the node if it not already is if !coupling.IsNodeInSlice(n, edge.To.Succ) { edge.To.Succ = append(edge.To.Succ, n) } } else { log.Printf("decreasing at cell (%v,%v)", edge.To.S, edge.To.T) edge.Prob -= min // if the line edge.Prob -= min, makes edge.Prob to zero, it is not a basic cell edge.Basic = !utils.ApproxEqual(edge.Prob, 0) // if no longer basic remove the main node as a successor for the node if !edge.Basic { coupling.DeleteNodeInSlice(n, &edge.To.Succ) n.BasicCount-- } } edge.To.Visited = false }
func updateNode(node *coupling.Node, newValues []float64) { if (len(node.Adj) * len(node.Adj[0])) != len(newValues) { log.Printf("%v %v", (len(node.Adj) * len(node.Adj[0])), len(newValues)) panic("The amount of new values does not match the adjacency matrix!") } k := 0 for i := range (*node).Adj { for _, edge := range (*node).Adj[i] { prevBasic := edge.Basic prob := newValues[k] if utils.ApproxEqual(prob, 0.0) { edge.Basic = false coupling.DeleteNodeInSlice(node, &edge.To.Succ) if prevBasic { node.BasicCount-- } } else { edge.Basic = true if !prevBasic { node.BasicCount++ } if !coupling.IsNodeInSlice(node, edge.To.Succ) { edge.To.Succ = append(edge.To.Succ, node) } } edge.Prob = prob k++ } } }
func TestCorrectLinearFunctionsCreated(t *testing.T) { c, m, visited, exact, d := coupling.SetUpTest() w := matching.FindFeasibleMatching(m, 0, 3, &c) setpair.Setpair(m, w, exact, visited, d, &c) a, b, index := setUpLinearEquationFrame() setUpLinearEquations(w, exact, d, &a, &b, 0, &index, 1.0) checkEquationDimensions(t, a, b, index, 2) assert.True(t, utils.ApproxEqual(b[0], 0.83)) assert.True(t, utils.ApproxEqual(b[1], 0.49)) assert.Equal(t, 1.0, a[0][0], "the value in the matrix diagonal was not 1.0") assert.True(t, utils.ApproxEqual(a[0][1], -0.17), "the value in the matrix was not -0.17") assert.Equal(t, 0.0, a[1][0], "the value in the matrix was not 0.0") assert.Equal(t, 1.0, a[1][1], "the value in the matrix diagonal was not 1.0") }
func TestCorrectLinearFunctionsCreatedLoops(t *testing.T) { c, m, visited, exact, d := coupling.SetUpTest() w := matching.FindFeasibleMatching(m, 0, 3, &c) setpair.Setpair(m, w, exact, visited, d, &c) a, b, index := setUpLinearEquationFrame() // inserting two loops into the coupling w.Adj[0][0] = &coupling.Edge{w, 0.33, true} w.Adj[2][2].To.Adj[1][2] = &coupling.Edge{w, 0.34, true} setUpLinearEquations(w, exact, d, &a, &b, 0, &index, 1.0) checkEquationDimensions(t, a, b, index, 2) assert.True(t, utils.ApproxEqual(b[0], 0.5)) assert.True(t, utils.ApproxEqual(b[1], 0.49)) assert.True(t, utils.ApproxEqual(a[0][0], 0.67), "the value in the matrix diagonal was not 0.67") assert.True(t, utils.ApproxEqual(a[0][1], -0.17), "the value in the matrix was not -0.17") assert.True(t, utils.ApproxEqual(a[1][0], -0.34), "the value in the matrix was not 0.0") assert.Equal(t, 1.0, a[1][1], "the value in the matrix diagonal was not 1.0") }
func TestCorrectNestedMatchingFound(t *testing.T) { expected := [][]float64{ []float64{0.33, 0.17, 0.0}, []float64{0.0, 0.16, 0.34}} // the same functions used for random matching testing c, m, visited, exact, d := coupling.SetUpTest() w := matching.FindFeasibleMatching(m, 0, 3, &c) Setpair(m, w, exact, visited, d, &c) node := w.Adj[2][2].To for i := 0; i < len(expected); i++ { for j := 0; j < len(expected[0]); j++ { assert.True(t, utils.ApproxEqual(expected[i][j], node.Adj[i][j].Prob), "the correct probability for cell (%v,%v) were not inserted", i, j) } } }
func findDemandedPairs(w *coupling.Node, visited [][]bool) []*coupling.Edge { demanded := make([]*coupling.Edge, 0) for _, rows := range w.Adj { for _, edge := range rows { if utils.ApproxEqual(edge.Prob, 0.0) { continue } if visited[edge.To.S][edge.To.T] || visited[edge.To.T][edge.To.S] { continue } demanded = append(demanded, edge) } } return demanded }
func TestCorrectMatchingFound(t *testing.T) { expected := [][]float64{ []float64{0.33, 0.0, 0.0}, []float64{0.0, 0.33, 0.0}, []float64{0.0, 0.0, 0.17}, []float64{0.0, 0.0, 0.17}} c := coupling.New() m := coupling.SetUpMarkov() w := FindFeasibleMatching(m, 0, 3, &c) for i := 0; i < len(expected); i++ { for j := 0; j < len(expected[0]); j++ { assert.True(t, utils.ApproxEqual(expected[i][j], w.Adj[i][j].Prob), "the correct probability were not inserted") } } }
func updateUntilOptimalSolutionsFound(lambda float64, m markov.MarkovChain, node *coupling.Node, exact [][]bool, visited [][]bool, d [][]float64, c coupling.Coupling, TPSolver func(markov.MarkovChain, *coupling.Node, [][]float64, float64, int, int), solvedNodes []*coupling.Node) { log.Printf("find optimal for: (%v,%v)", node.S, node.T) min, i, j := uvmethod.Run(node, d) // if min is negative, we can further improve it, so we update it using the TPSolver and iterated until we cannot improve it further for min < 0 && !utils.ApproxEqual(min, 0) { previ, prevj := i, j TPSolver(m, node, d, min, i, j) setpair.Setpair(m, node, exact, visited, d, &c) disc.Disc(lambda, node, exact, d, &c) min, i, j = uvmethod.Run(node, d) if previ == i && prevj == j && min < 0 { break } } // append solved nodes such that we do not end up recurively calling nodes that have already been found to be optimal solvedNodes = append(solvedNodes, node) for _, row := range node.Adj { for _, edge := range row { if edge.To.Adj == nil { // if the node do not have an adjacency matrix or is exact, we do not have to proccess it continue } if coupling.IsNodeInSlice(edge.To, solvedNodes) { // if the node has already been proccesses, we do not have to do it again continue } updateUntilOptimalSolutionsFound(lambda, m, edge.To, exact, visited, d, c, TPSolver, solvedNodes) } } exact[node.S][node.T] = true exact[node.S][node.T] = true return }
func visit(root *Node, results []*Node) []*Node { // log.Printf("%s, %s", root.S, root.T) if root.Adj == nil { return results } for i := range root.Adj { for j := range root.Adj[0] { edge := root.Adj[i][j] toVisit := edge.To if !edge.Basic || toVisit.Visited { continue } else if !utils.ApproxEqual(edge.Prob, 0) { toVisit.Visited = true results = append(results, toVisit) results = visit(toVisit, results) } } } return results }
func TestOptimalSolutionFound(t *testing.T) { c, m, visited, exact, d := coupling.SetUpTest() d = sets.InitD(len(m.Transitions)) w := matching.FindFeasibleMatching(m, 0, 3, &c) setpair.Setpair(m, w, exact, visited, d, &c) // the expected solution is not precice since the markov chain used do not use precise fractions expected := [][]float64{ []float64{0, 0.33, 0}, []float64{0, 0, 0.33}, []float64{0.17, 0, 0}, []float64{0.16, 0, 0.01}} min, i, j := uvmethod.Run(w, d) Solve(m, w, d, min, i, j) for i := range w.Adj { for j := range w.Adj[0] { assert.True(t, utils.ApproxEqual(expected[i][j], w.Adj[i][j].Prob), "the optimal probability found was not what we expected") } } }
func FindFeasibleMatching(m markov.MarkovChain, u int, v int, c *coupling.Coupling) *coupling.Node { n := len(m.Transitions[u]) u, v = utils.GetMinMax(u, v) // tries to find the node (u,v) in c, if not make a new one and add to c node := coupling.FindNode(u, v, c) if node == nil { node = &coupling.Node{S: u, T: v, Succ: []*coupling.Node{}} c.Nodes = append(c.Nodes, node) } log.Printf("copy the transitions from the states %v and %v", u, v) uTransitions := make([]float64, n, n) vTransitions := make([]float64, n, n) // copies the transitions of u and v such that we do not makes changes in the markov chain copy(uTransitions, m.Transitions[u]) copy(vTransitions, m.Transitions[v]) // finds the length of the rows and columns in the matching for u and v lenrow, lencol := matchingDimensions(uTransitions, vTransitions, n) log.Printf("row and column length are: %v and %v", lenrow, lencol) rowindex := make([]int, lenrow, lenrow) colindex := make([]int, lencol, lencol) // finds the row and column indexs for the u and v matching setMatchingIndexes(uTransitions, vTransitions, n, rowindex, colindex) log.Printf("row index: %s", rowindex) log.Printf("column index: %s", colindex) matching := make([][]*coupling.Edge, lenrow, lenrow) for i := range matching { matching[i] = make([]*coupling.Edge, lencol, lencol) } // fills out the matching with node pointers, using nodes already in the coupling c if they exist filloutAdj(rowindex, colindex, lenrow, lencol, matching, c) // completes the matching by inserting probabilities and setting appropriate cells to basic i, j := 0, 0 for i < lenrow && j < lencol { if utils.ApproxEqual(uTransitions[rowindex[i]], vTransitions[colindex[j]]) { matching[i][j].Prob = uTransitions[rowindex[i]] // check if we are in the lower right corner, such that we do not get an out of bounds error if !(i+1 == lenrow && j+1 == lencol) { matching[i][j+1].Basic = true node.BasicCount++ } matching[i][j].Basic = true matching[i][j].To.Succ = append(matching[i][j].To.Succ, node) node.BasicCount++ i++ j++ } else if uTransitions[rowindex[i]] < vTransitions[colindex[j]] { matching[i][j].Prob = uTransitions[rowindex[i]] vTransitions[colindex[j]] = vTransitions[colindex[j]] - uTransitions[rowindex[i]] matching[i][j].Basic = true matching[i][j].To.Succ = append(matching[i][j].To.Succ, node) node.BasicCount++ i++ } else { matching[i][j].Prob = vTransitions[colindex[j]] uTransitions[rowindex[i]] = uTransitions[rowindex[i]] - vTransitions[colindex[j]] matching[i][j].Basic = true matching[i][j].To.Succ = append(matching[i][j].To.Succ, node) node.BasicCount++ j++ } } log.Println("Logging matching:") for i := 0; i < lenrow; i++ { for j := 0; j < lencol; j++ { log.Printf(" - At: u %d, %d prob: %f, basic: %t", matching[i][j].To.S, matching[i][j].To.T, matching[i][j].Prob, matching[i][j].Basic) } } node.Adj = matching return node }