public void TestUncontracted() { var edge = new CHEdgeData(123, true, true, true, 123.45f); Assert.AreEqual(7, edge.Meta); Assert.AreEqual(123.45f, edge.Weight); Assert.AreEqual(123, edge.Value); Assert.AreEqual(123, edge.Tags); Assert.AreEqual(true, edge.Forward); Assert.AreEqual(true, edge.CanMoveBackward); Assert.AreEqual(true, edge.CanMoveForward); Assert.AreEqual(uint.MaxValue, edge.ContractedId); Assert.IsFalse(edge.IsContracted); Assert.IsTrue(edge.RepresentsNeighbourRelations); edge = new CHEdgeData(123, false, true, true, 123.45f); Assert.AreEqual(3, edge.Meta); Assert.AreEqual(123.45f, edge.Weight); Assert.AreEqual(123, edge.Value); Assert.AreEqual(123, edge.Tags); Assert.AreEqual(false, edge.Forward); Assert.AreEqual(true, edge.CanMoveForward); Assert.AreEqual(true, edge.CanMoveBackward); Assert.AreEqual(uint.MaxValue, edge.ContractedId); Assert.IsFalse(edge.IsContracted); Assert.IsTrue(edge.RepresentsNeighbourRelations); edge = new CHEdgeData(123, true, false, true, 123.45f); Assert.AreEqual(6, edge.Meta); Assert.AreEqual(123.45f, edge.Weight); Assert.AreEqual(123, edge.Value); Assert.AreEqual(123, edge.Tags); Assert.AreEqual(true, edge.Forward); Assert.AreEqual(false, edge.CanMoveForward); Assert.AreEqual(true, edge.CanMoveBackward); Assert.AreEqual(uint.MaxValue, edge.ContractedId); Assert.IsFalse(edge.IsContracted); Assert.IsTrue(edge.RepresentsNeighbourRelations); edge = new CHEdgeData(123, true, true, false, 123.45f); Assert.AreEqual(5, edge.Meta); Assert.AreEqual(123.45f, edge.Weight); Assert.AreEqual(123, edge.Value); Assert.AreEqual(123, edge.Tags); Assert.AreEqual(true, edge.Forward); Assert.AreEqual(true, edge.CanMoveForward); Assert.AreEqual(false, edge.CanMoveBackward); Assert.AreEqual(uint.MaxValue, edge.ContractedId); Assert.IsFalse(edge.IsContracted); Assert.IsTrue(edge.RepresentsNeighbourRelations); }
/// <summary> /// Contracts the given vertex. /// </summary> /// <param name="vertex"></param> public void Contract(uint vertex) { if (this.IsContracted(vertex)) { throw new Exception("Is already contracted!"); } // get all information from the source. var edges = _target.GetEdges(vertex).ToList(); // report the before contraction event. this.OnBeforeContraction(vertex, edges); // build the list of edges to replace. var allNeigbours = new List <Edge <CHEdgeData> >(edges.Count); var tos = new List <uint>(edges.Count); var tosSet = new HashSet <uint>(); foreach (var edge in edges) { // use this edge for contraction. allNeigbours.Add(edge); tos.Add(edge.Neighbour); tosSet.Add(edge.Neighbour); // remove the edge in downwards direction and on the edge with the same data. _target.RemoveEdge(edge.Neighbour, vertex); } //// build the list of pairs and make sure duplicates don't count. //var allNeighbourPairs = new Dictionary<Tuple<uint, uint>, Tuple<float, float>>(); //for(int x = 1; x < allNeigbours.Count; x++) //{ // var xEdge = allNeigbours[x]; // var xEdgeForwardWeight = xEdge.EdgeData.CanMoveBackward ? xEdge.EdgeData.Weight : float.MaxValue; // var xEdgeBackwardWeight = xEdge.EdgeData.CanMoveForward ? xEdge.EdgeData.Weight : float.MaxValue; // for(int y = 0; y < x; y++) // { // var yEdge = allNeigbours[x]; // //var yEdgeForwardWeight = yEdge.EdgeData.CanMoveBackward ? yEdge.EdgeData.Weight : float.MaxValue; // //var yEdgeBackwardWeight = yEdge.EdgeData.CanMoveForward ? yEdge.EdgeData.Weight : float.MaxValue; // float forwardWeight = float.MaxValue; // float backwardWeight = float.MaxValue; // if(xEdge.Neighbour < yEdge.Neighbour) // { // if (xEdge.EdgeData.CanMoveBackward && yEdge.EdgeData.CanMoveForward) // { // forwardWeight = xEdgeBackwardWeight + yEdge.EdgeData.Weight; // } // if (xEdge.EdgeData.CanMoveForward && yEdge.EdgeData.CanMoveBackward) // { // backwardWeight = xEdgeForwardWeight + yEdge.EdgeData.Weight; // } // } // else if(xEdge.Neighbour > yEdge.Neighbour) // { // if (xEdge.EdgeData.CanMoveBackward && yEdge.EdgeData.CanMoveForward) // { // backwardWeight = xEdgeBackwardWeight + yEdge.EdgeData.Weight; // } // if (xEdge.EdgeData.CanMoveForward && yEdge.EdgeData.CanMoveBackward) // { // forwardWeight = xEdgeForwardWeight + yEdge.EdgeData.Weight; // } // } // Tuple<float, float> existingWeights; // Tuple<uint, uint> neighbourPair = new Tuple<uint,uint>(xEdge.Neighbour, yEdge.Neighbour); // if(!allNeighbourPairs.TryGetValue(neighbourPair, out existingWeights)) // { // if (existingWeights.Item1 < forwardWeight) // { // forwardWeight = existingWeights.Item1; // } // if (existingWeights.Item2 < backwardWeight) // { // backwardWeight = existingWeights.Item2; // } // } // allNeighbourPairs[neighbourPair] = new Tuple<float, float>(forwardWeight, backwardWeight); // } //} var toRequeue = new HashSet <uint>(); var forwardEdges = new CHEdgeData?[2]; var backwardEdges = new CHEdgeData?[2]; var existingEdgesToRemove = new HashSet <CHEdgeData>(); // loop over each combination of edges just once. var forwardWitnesses = new bool[allNeigbours.Count]; var backwardWitnesses = new bool[allNeigbours.Count]; var weights = new List <float>(allNeigbours.Count); var edgesToY = new Dictionary <uint, Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float> >(allNeigbours.Count); for (int x = 1; x < allNeigbours.Count; x++) { // loop over all elements first. var xEdge = allNeigbours[x]; // get edges. edgesToY.Clear(); var rawEdgesToY = _target.GetEdges(xEdge.Neighbour); while (rawEdgesToY.MoveNext()) { var rawEdgeNeighbour = rawEdgesToY.Neighbour; if (tosSet.Contains(rawEdgeNeighbour)) { var rawEdgeData = rawEdgesToY.EdgeData; var rawEdgeForwardWeight = rawEdgeData.CanMoveForward ? rawEdgeData.Weight : float.MaxValue; var rawEdgeBackwardWeight = rawEdgeData.CanMoveBackward ? rawEdgeData.Weight : float.MaxValue; Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float> edgeTuple; if (!edgesToY.TryGetValue(rawEdgeNeighbour, out edgeTuple)) { edgeTuple = new Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>(rawEdgeData, null, null, rawEdgeForwardWeight, rawEdgeBackwardWeight); edgesToY.Add(rawEdgeNeighbour, edgeTuple); } else if (!edgeTuple.Item2.HasValue) { edgesToY[rawEdgeNeighbour] = new Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>( edgeTuple.Item1, rawEdgeData, null, rawEdgeForwardWeight < edgeTuple.Item4 ? rawEdgeForwardWeight : edgeTuple.Item4, rawEdgeBackwardWeight < edgeTuple.Item5 ? rawEdgeBackwardWeight : edgeTuple.Item5); } else { edgesToY[rawEdgeNeighbour] = new Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>( edgeTuple.Item1, edgeTuple.Item2, rawEdgeData, rawEdgeForwardWeight < edgeTuple.Item4 ? rawEdgeForwardWeight : edgeTuple.Item4, rawEdgeBackwardWeight < edgeTuple.Item5 ? rawEdgeBackwardWeight : edgeTuple.Item5); } } } // calculate max weight. weights.Clear(); var forwardUnknown = false; var backwardUnknown = false; for (int y = 0; y < x; y++) { // update maxWeight. var yEdge = allNeigbours[y]; if (xEdge.Neighbour != yEdge.Neighbour) { // reset witnesses. var forwardWeight = (float)xEdge.EdgeData.Weight + (float)yEdge.EdgeData.Weight; forwardWitnesses[y] = !xEdge.EdgeData.CanMoveBackward || !yEdge.EdgeData.CanMoveForward; backwardWitnesses[y] = !xEdge.EdgeData.CanMoveForward || !yEdge.EdgeData.CanMoveBackward; weights.Add(forwardWeight); Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float> edgeTuple; if (edgesToY.TryGetValue(yEdge.Neighbour, out edgeTuple)) { if (!forwardWitnesses[y]) { // check 1-hop witnesses. if (edgeTuple.Item4 <= forwardWeight) { forwardWitnesses[y] = true; } } if (!backwardWitnesses[y]) { // check 1-hop witnesses. if (edgeTuple.Item5 <= forwardWeight) { backwardWitnesses[y] = true; } } } forwardUnknown = !forwardWitnesses[y] || forwardUnknown; backwardUnknown = !backwardWitnesses[y] || backwardUnknown; } else { // already set this to true, not use calculating it's witness. forwardWitnesses[y] = true; backwardWitnesses[y] = true; weights.Add(0); } } // calculate witnesses. if (forwardUnknown || backwardUnknown) { _contractionWitnessCalculator.Exists(_target, xEdge.Neighbour, tos, weights, int.MaxValue, ref forwardWitnesses, ref backwardWitnesses); } for (int y = 0; y < x; y++) { // loop over all elements. var yEdge = allNeigbours[y]; // add the combinations of these edges. if (xEdge.Neighbour != yEdge.Neighbour) { // there is a connection from x to y and there is no witness path. // create x-to-y data and edge. var canMoveForward = !forwardWitnesses[y] && (xEdge.EdgeData.CanMoveBackward && yEdge.EdgeData.CanMoveForward); var canMoveBackward = !backwardWitnesses[y] && (xEdge.EdgeData.CanMoveForward && yEdge.EdgeData.CanMoveBackward); if (canMoveForward || canMoveBackward) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. // calculate the total weights. var weight = (float)xEdge.EdgeData.Weight + (float)yEdge.EdgeData.Weight; // there are a few options now: // 1) No edges yet between xEdge.Neighbour and yEdge.Neighbour. // 1) There is no other contracted edge: just add as a duplicate. // 2) There is at least on other contracted edge: optimize information because there can only be 4 case between two vertices: // - One bidirectional edge. // - Two directed edges with different weights. // - One forward edge. // - One backward edge. // => all available information needs to be combined. // check existing data. var existingForwardWeight = float.MaxValue; var existingBackwardWeight = float.MaxValue; uint existingForwardContracted = 0; uint existingBackwardContracted = 0; var existingCanMoveForward = false; var existingCanMoveBackward = false; var existingEdges = _target.GetEdges(xEdge.Neighbour, yEdge.Neighbour); existingEdgesToRemove.Clear(); while (existingEdges.MoveNext()) { var existingEdgeData = existingEdges.EdgeData; if (existingEdgeData.IsContracted) { // this edge is contracted, collect it's information. existingEdgesToRemove.Add(existingEdgeData); if (existingEdgeData.CanMoveForward) { // can move forward, so at least one edge that can move forward. existingCanMoveForward = true; if (existingForwardWeight > existingEdgeData.Weight) { // update forward weight. existingForwardWeight = existingEdgeData.Weight; existingForwardContracted = existingEdgeData.ContractedId; } } if (existingEdgeData.CanMoveBackward) { // can move backward, so at least one edge that can move backward. existingCanMoveBackward = true; if (existingBackwardWeight > existingEdgeData.Weight) { // update backward weight. existingBackwardWeight = existingEdgeData.Weight; existingBackwardContracted = existingEdgeData.ContractedId; } } } } if (existingCanMoveForward || existingCanMoveBackward) { // there is already another contraced edge. uint forwardContractedId = vertex; float forwardWeight = weight; // merge with existing data. if (existingCanMoveForward && ((weight > existingForwardWeight) || !canMoveForward)) { // choose the smallest weight. canMoveForward = true; forwardContractedId = existingForwardContracted; forwardWeight = existingForwardWeight; } uint backwardContractedId = vertex; float backwardWeight = weight; // merge with existing data. if (existingCanMoveBackward && ((weight > existingBackwardWeight) || !canMoveBackward)) { // choose the smallest weight. canMoveBackward = true; backwardContractedId = existingBackwardContracted; backwardWeight = existingBackwardWeight; } // add one of the 4 above case. forwardEdges[0] = null; forwardEdges[1] = null; backwardEdges[0] = null; backwardEdges[1] = null; if (canMoveForward && canMoveBackward && forwardWeight == backwardWeight && forwardContractedId == backwardContractedId) { // just add one edge. forwardEdges[0] = new CHEdgeData(forwardContractedId, true, true, forwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(forwardContractedId, true, true, forwardWeight)); backwardEdges[0] = new CHEdgeData(backwardContractedId, true, true, backwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(backwardContractedId, true, true, backwardWeight)); } else if (canMoveBackward && canMoveForward) { // add two different edges. forwardEdges[0] = new CHEdgeData(forwardContractedId, true, false, forwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(forwardContractedId, true, false, forwardWeight)); backwardEdges[0] = new CHEdgeData(forwardContractedId, false, true, forwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(forwardContractedId, false, true, forwardWeight)); forwardEdges[1] = new CHEdgeData(backwardContractedId, false, true, backwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(backwardContractedId, false, true, backwardWeight)); backwardEdges[1] = new CHEdgeData(backwardContractedId, true, false, backwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(backwardContractedId, true, false, backwardWeight)); } else if (canMoveForward) { // only add one forward edge. forwardEdges[0] = new CHEdgeData(forwardContractedId, true, false, forwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(forwardContractedId, true, false, forwardWeight)); backwardEdges[0] = new CHEdgeData(forwardContractedId, false, true, forwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(forwardContractedId, false, true, forwardWeight)); } else if (canMoveBackward) { // only add one backward edge. forwardEdges[0] = new CHEdgeData(backwardContractedId, false, true, backwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(backwardContractedId, false, true, backwardWeight)); backwardEdges[0] = new CHEdgeData(backwardContractedId, true, false, backwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(backwardContractedId, true, false, backwardWeight)); } // remove all existing stuff. foreach (var existingEdgeToRemove in existingEdgesToRemove) { if (forwardEdges[0].Equals(existingEdgeToRemove)) { // this forward edge is to be kept. forwardEdges[0] = null; // it's already there. } else if (forwardEdges[1] != null && !forwardEdges[1].Equals(existingEdgeToRemove)) { // this forward edge is to be kept. forwardEdges[1] = null; // it's already there. } else { // yup, just remove it now. _target.RemoveEdge(xEdge.Neighbour, yEdge.Neighbour, existingEdgeToRemove); } var existingEdgeToRemoveBackward = (CHEdgeData)existingEdgeToRemove.Reverse(); if (backwardEdges[0].Equals(existingEdgeToRemoveBackward)) { // this backward edge is to be kept. backwardEdges[0] = null; // it's already there. } else if (backwardEdges[1] != null && !backwardEdges[1].Equals(existingEdgeToRemoveBackward)) { // this backward edge is to be kept. backwardEdges[1] = null; // it's already there. } else { // yup, just remove it now. _target.RemoveEdge(yEdge.Neighbour, xEdge.Neighbour, existingEdgeToRemoveBackward); } } // add remaining edges. if (forwardEdges[0].HasValue) { _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, forwardEdges[0].Value); } if (forwardEdges[1].HasValue) { _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, forwardEdges[1].Value); } if (backwardEdges[0].HasValue) { _target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, backwardEdges[0].Value); } if (backwardEdges[1].HasValue) { _target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, backwardEdges[1].Value); } toRequeue.Add(xEdge.Neighbour); toRequeue.Add(yEdge.Neighbour); } else { // there is no edge, just add the data. // add contracted edges like normal. _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(vertex, canMoveForward, canMoveBackward, weight)); _target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(vertex, canMoveBackward, canMoveForward, weight)); toRequeue.Add(xEdge.Neighbour); toRequeue.Add(yEdge.Neighbour); } } } } } // mark the vertex as contracted. this.MarkContracted(vertex); // notify a contracted neighbour. _calculator.NotifyContracted(vertex); // report the after contraction event. this.OnAfterContraction(vertex, allNeigbours); //// update priority of direct neighbours. //foreach (var neighbour in toRequeue) //{ // this.ReQueue(neighbour); //} }
/// <summary> /// Contracts the given vertex. /// </summary> /// <param name="vertex"></param> public void Contract(uint vertex) { if (this.IsContracted(vertex)) { throw new Exception("Is already contracted!"); } // get all information from the source. var edges = _target.GetEdges(vertex).ToList(); // report the before contraction event. this.OnBeforeContraction(vertex, edges); // build the list of edges to replace. var allNeigbours = new List<Edge<CHEdgeData>>(edges.Count); var tos = new List<uint>(edges.Count); var tosSet = new HashSet<uint>(); foreach (var edge in edges) { // use this edge for contraction. allNeigbours.Add(edge); tos.Add(edge.Neighbour); tosSet.Add(edge.Neighbour); // remove the edge in downwards direction and on the edge with the same data. _target.RemoveEdge(edge.Neighbour, vertex); } //// build the list of pairs and make sure duplicates don't count. //var allNeighbourPairs = new Dictionary<Tuple<uint, uint>, Tuple<float, float>>(); //for(int x = 1; x < allNeigbours.Count; x++) //{ // var xEdge = allNeigbours[x]; // var xEdgeForwardWeight = xEdge.EdgeData.CanMoveBackward ? xEdge.EdgeData.Weight : float.MaxValue; // var xEdgeBackwardWeight = xEdge.EdgeData.CanMoveForward ? xEdge.EdgeData.Weight : float.MaxValue; // for(int y = 0; y < x; y++) // { // var yEdge = allNeigbours[x]; // //var yEdgeForwardWeight = yEdge.EdgeData.CanMoveBackward ? yEdge.EdgeData.Weight : float.MaxValue; // //var yEdgeBackwardWeight = yEdge.EdgeData.CanMoveForward ? yEdge.EdgeData.Weight : float.MaxValue; // float forwardWeight = float.MaxValue; // float backwardWeight = float.MaxValue; // if(xEdge.Neighbour < yEdge.Neighbour) // { // if (xEdge.EdgeData.CanMoveBackward && yEdge.EdgeData.CanMoveForward) // { // forwardWeight = xEdgeBackwardWeight + yEdge.EdgeData.Weight; // } // if (xEdge.EdgeData.CanMoveForward && yEdge.EdgeData.CanMoveBackward) // { // backwardWeight = xEdgeForwardWeight + yEdge.EdgeData.Weight; // } // } // else if(xEdge.Neighbour > yEdge.Neighbour) // { // if (xEdge.EdgeData.CanMoveBackward && yEdge.EdgeData.CanMoveForward) // { // backwardWeight = xEdgeBackwardWeight + yEdge.EdgeData.Weight; // } // if (xEdge.EdgeData.CanMoveForward && yEdge.EdgeData.CanMoveBackward) // { // forwardWeight = xEdgeForwardWeight + yEdge.EdgeData.Weight; // } // } // Tuple<float, float> existingWeights; // Tuple<uint, uint> neighbourPair = new Tuple<uint,uint>(xEdge.Neighbour, yEdge.Neighbour); // if(!allNeighbourPairs.TryGetValue(neighbourPair, out existingWeights)) // { // if (existingWeights.Item1 < forwardWeight) // { // forwardWeight = existingWeights.Item1; // } // if (existingWeights.Item2 < backwardWeight) // { // backwardWeight = existingWeights.Item2; // } // } // allNeighbourPairs[neighbourPair] = new Tuple<float, float>(forwardWeight, backwardWeight); // } //} var toRequeue = new HashSet<uint>(); var forwardEdges = new CHEdgeData?[2]; var backwardEdges = new CHEdgeData?[2]; var existingEdgesToRemove = new HashSet<CHEdgeData>(); // loop over each combination of edges just once. var forwardWitnesses = new bool[allNeigbours.Count]; var backwardWitnesses = new bool[allNeigbours.Count]; var weights = new List<float>(allNeigbours.Count); var edgesToY = new Dictionary<uint, Tuple<CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>>(allNeigbours.Count); for (int x = 1; x < allNeigbours.Count; x++) { // loop over all elements first. var xEdge = allNeigbours[x]; // get edges. edgesToY.Clear(); var rawEdgesToY = _target.GetEdges(xEdge.Neighbour); while (rawEdgesToY.MoveNext()) { var rawEdgeNeighbour = rawEdgesToY.Neighbour; if (tosSet.Contains(rawEdgeNeighbour)) { var rawEdgeData = rawEdgesToY.EdgeData; var rawEdgeForwardWeight = rawEdgeData.CanMoveForward ? rawEdgeData.Weight : float.MaxValue; var rawEdgeBackwardWeight = rawEdgeData.CanMoveBackward ? rawEdgeData.Weight : float.MaxValue; Tuple<CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float> edgeTuple; if (!edgesToY.TryGetValue(rawEdgeNeighbour, out edgeTuple)) { edgeTuple = new Tuple<CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>(rawEdgeData, null, null, rawEdgeForwardWeight, rawEdgeBackwardWeight); edgesToY.Add(rawEdgeNeighbour, edgeTuple); } else if (!edgeTuple.Item2.HasValue) { edgesToY[rawEdgeNeighbour] = new Tuple<CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>( edgeTuple.Item1, rawEdgeData, null, rawEdgeForwardWeight < edgeTuple.Item4 ? rawEdgeForwardWeight : edgeTuple.Item4, rawEdgeBackwardWeight < edgeTuple.Item5 ? rawEdgeBackwardWeight : edgeTuple.Item5); } else { edgesToY[rawEdgeNeighbour] = new Tuple<CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>( edgeTuple.Item1, edgeTuple.Item2, rawEdgeData, rawEdgeForwardWeight < edgeTuple.Item4 ? rawEdgeForwardWeight : edgeTuple.Item4, rawEdgeBackwardWeight < edgeTuple.Item5 ? rawEdgeBackwardWeight : edgeTuple.Item5); } } } // calculate max weight. weights.Clear(); var forwardUnknown = false; var backwardUnknown = false; for (int y = 0; y < x; y++) { // update maxWeight. var yEdge = allNeigbours[y]; if (xEdge.Neighbour != yEdge.Neighbour) { // reset witnesses. var forwardWeight = (float)xEdge.EdgeData.Weight + (float)yEdge.EdgeData.Weight; forwardWitnesses[y] = !xEdge.EdgeData.CanMoveBackward || !yEdge.EdgeData.CanMoveForward; backwardWitnesses[y] = !xEdge.EdgeData.CanMoveForward || !yEdge.EdgeData.CanMoveBackward; weights.Add(forwardWeight); Tuple<CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float> edgeTuple; if (edgesToY.TryGetValue(yEdge.Neighbour, out edgeTuple)) { if (!forwardWitnesses[y]) { // check 1-hop witnesses. if (edgeTuple.Item4 <= forwardWeight) { forwardWitnesses[y] = true; } } if (!backwardWitnesses[y]) { // check 1-hop witnesses. if (edgeTuple.Item5 <= forwardWeight) { backwardWitnesses[y] = true; } } } forwardUnknown = !forwardWitnesses[y] || forwardUnknown; backwardUnknown = !backwardWitnesses[y] || backwardUnknown; } else { // already set this to true, not use calculating it's witness. forwardWitnesses[y] = true; backwardWitnesses[y] = true; weights.Add(0); } } // calculate witnesses. if (forwardUnknown || backwardUnknown) { _contractionWitnessCalculator.Exists(_target, xEdge.Neighbour, tos, weights, int.MaxValue, ref forwardWitnesses, ref backwardWitnesses); } for (int y = 0; y < x; y++) { // loop over all elements. var yEdge = allNeigbours[y]; // add the combinations of these edges. if (xEdge.Neighbour != yEdge.Neighbour) { // there is a connection from x to y and there is no witness path. // create x-to-y data and edge. var canMoveForward = !forwardWitnesses[y] && (xEdge.EdgeData.CanMoveBackward && yEdge.EdgeData.CanMoveForward); var canMoveBackward = !backwardWitnesses[y] && (xEdge.EdgeData.CanMoveForward && yEdge.EdgeData.CanMoveBackward); if (canMoveForward || canMoveBackward) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. // calculate the total weights. var weight = (float)xEdge.EdgeData.Weight + (float)yEdge.EdgeData.Weight; // there are a few options now: // 1) No edges yet between xEdge.Neighbour and yEdge.Neighbour. // 1) There is no other contracted edge: just add as a duplicate. // 2) There is at least on other contracted edge: optimize information because there can only be 4 case between two vertices: // - One bidirectional edge. // - Two directed edges with different weights. // - One forward edge. // - One backward edge. // => all available information needs to be combined. // check existing data. var existingForwardWeight = float.MaxValue; var existingBackwardWeight = float.MaxValue; uint existingForwardContracted = 0; uint existingBackwardContracted = 0; var existingCanMoveForward = false; var existingCanMoveBackward = false; var existingEdges = _target.GetEdges(xEdge.Neighbour, yEdge.Neighbour); existingEdgesToRemove.Clear(); while(existingEdges.MoveNext()) { var existingEdgeData = existingEdges.EdgeData; if(existingEdgeData.IsContracted) { // this edge is contracted, collect it's information. existingEdgesToRemove.Add(existingEdgeData); if(existingEdgeData.CanMoveForward) { // can move forward, so at least one edge that can move forward. existingCanMoveForward = true; if (existingForwardWeight > existingEdgeData.Weight) { // update forward weight. existingForwardWeight = existingEdgeData.Weight; existingForwardContracted = existingEdgeData.ContractedId; } } if (existingEdgeData.CanMoveBackward) { // can move backward, so at least one edge that can move backward. existingCanMoveBackward = true; if (existingBackwardWeight > existingEdgeData.Weight) { // update backward weight. existingBackwardWeight = existingEdgeData.Weight; existingBackwardContracted = existingEdgeData.ContractedId; } } } } if (existingCanMoveForward || existingCanMoveBackward) { // there is already another contraced edge. uint forwardContractedId = vertex; float forwardWeight = weight; // merge with existing data. if (existingCanMoveForward && ((weight > existingForwardWeight) || !canMoveForward)) { // choose the smallest weight. canMoveForward = true; forwardContractedId = existingForwardContracted; forwardWeight = existingForwardWeight; } uint backwardContractedId = vertex; float backwardWeight = weight; // merge with existing data. if (existingCanMoveBackward && ((weight > existingBackwardWeight) || !canMoveBackward)) { // choose the smallest weight. canMoveBackward = true; backwardContractedId = existingBackwardContracted; backwardWeight = existingBackwardWeight; } // add one of the 4 above case. forwardEdges[0] = null; forwardEdges[1] = null; backwardEdges[0] = null; backwardEdges[1] = null; if (canMoveForward && canMoveBackward && forwardWeight == backwardWeight && forwardContractedId == backwardContractedId) { // just add one edge. forwardEdges[0] = new CHEdgeData(forwardContractedId, true, true, forwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(forwardContractedId, true, true, forwardWeight)); backwardEdges[0] = new CHEdgeData(backwardContractedId, true, true, backwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(backwardContractedId, true, true, backwardWeight)); } else if (canMoveBackward && canMoveForward) { // add two different edges. forwardEdges[0] = new CHEdgeData(forwardContractedId, true, false, forwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(forwardContractedId, true, false, forwardWeight)); backwardEdges[0] = new CHEdgeData(forwardContractedId, false, true, forwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(forwardContractedId, false, true, forwardWeight)); forwardEdges[1] = new CHEdgeData(backwardContractedId, false, true, backwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(backwardContractedId, false, true, backwardWeight)); backwardEdges[1] = new CHEdgeData(backwardContractedId, true, false, backwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(backwardContractedId, true, false, backwardWeight)); } else if (canMoveForward) { // only add one forward edge. forwardEdges[0] = new CHEdgeData(forwardContractedId, true, false, forwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(forwardContractedId, true, false, forwardWeight)); backwardEdges[0] = new CHEdgeData(forwardContractedId, false, true, forwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(forwardContractedId, false, true, forwardWeight)); } else if (canMoveBackward) { // only add one backward edge. forwardEdges[0] = new CHEdgeData(backwardContractedId, false, true, backwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(backwardContractedId, false, true, backwardWeight)); backwardEdges[0] = new CHEdgeData(backwardContractedId, true, false, backwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(backwardContractedId, true, false, backwardWeight)); } // remove all existing stuff. foreach (var existingEdgeToRemove in existingEdgesToRemove) { if (forwardEdges[0].Equals(existingEdgeToRemove)) { // this forward edge is to be kept. forwardEdges[0] = null; // it's already there. } else if(forwardEdges[1] != null && !forwardEdges[1].Equals(existingEdgeToRemove)) { // this forward edge is to be kept. forwardEdges[1] = null; // it's already there. } else { // yup, just remove it now. _target.RemoveEdge(xEdge.Neighbour, yEdge.Neighbour, existingEdgeToRemove); } var existingEdgeToRemoveBackward = (CHEdgeData)existingEdgeToRemove.Reverse(); if (backwardEdges[0].Equals(existingEdgeToRemoveBackward)) { // this backward edge is to be kept. backwardEdges[0] = null; // it's already there. } else if (backwardEdges[1] != null && !backwardEdges[1].Equals(existingEdgeToRemoveBackward)) { // this backward edge is to be kept. backwardEdges[1] = null; // it's already there. } else { // yup, just remove it now. _target.RemoveEdge(yEdge.Neighbour, xEdge.Neighbour, existingEdgeToRemoveBackward); } } // add remaining edges. if (forwardEdges[0].HasValue) { _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, forwardEdges[0].Value); } if (forwardEdges[1].HasValue) { _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, forwardEdges[1].Value); } if (backwardEdges[0].HasValue) { _target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, backwardEdges[0].Value); } if (backwardEdges[1].HasValue) { _target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, backwardEdges[1].Value); } toRequeue.Add(xEdge.Neighbour); toRequeue.Add(yEdge.Neighbour); } else { // there is no edge, just add the data. // add contracted edges like normal. _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(vertex, canMoveForward, canMoveBackward, weight)); _target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(vertex, canMoveBackward, canMoveForward, weight)); toRequeue.Add(xEdge.Neighbour); toRequeue.Add(yEdge.Neighbour); } } } } } // mark the vertex as contracted. this.MarkContracted(vertex); // notify a contracted neighbour. _calculator.NotifyContracted(vertex); // report the after contraction event. this.OnAfterContraction(vertex, allNeigbours); //// update priority of direct neighbours. //foreach (var neighbour in toRequeue) //{ // this.ReQueue(neighbour); //} }