/// <summary> /// Converts this given edge to a purely informative edge. /// </summary> /// <returns></returns> public CHEdgeData ConvertToInformative() { CHEdgeData informativeData = new CHEdgeData(); informativeData.Direction = this.Direction; informativeData.ContractedVertexId = this.ContractedVertexId; informativeData.Tags = this.Tags; informativeData.Weight = this.Weight; // convert the direction. if (informativeData.Direction <= 3) { informativeData.Direction = (byte)(informativeData.Direction + 12); } else if (informativeData.Direction <= 7) { informativeData.Direction = (byte)(informativeData.Direction + 8); } else if (informativeData.Direction <= 11) { informativeData.Direction = (byte)(informativeData.Direction + 4); } else { // edge was already informative. throw new InvalidCastException("Edge was already informative!"); } return(informativeData); }
/// <summary> /// Creates the exact reverse of this edge. /// </summary> /// <returns></returns> public IDynamicGraphEdgeData Reverse() { // to higher vertex: ( 0=bidirectional, 1=forward, 2=backward, 3=not forward and not backward). // to lower vertex: ( 4=bidirectional, 5=forward, 6=backward, 7=not forward and not backward). // no low/high info: ( 8=bidirectional, 9=forward, 10=backward, 11=not forward and not backward). var data = new CHEdgeData(); data.Tags = this.Tags; data.ContractedVertexId = this.ContractedVertexId; data.Weight = this.Weight; data.Direction = this.Direction; switch (this.Direction) { case 0: // to higher bidirectional. data.Direction = 4; // to lower bidirectional. break; case 1: // to higher forward. data.Direction = 6; // to lower backward. break; case 2: // to higher backward. data.Direction = 5; // to lower forward. break; case 3: // to higher no directional info. data.Direction = 7; // to lower no directional info. break; case 4: // to lower bidirectional. data.Direction = 0; // to higher bidirectional. break; case 5: // to lower forward. data.Direction = 2; // to higher backward. break; case 6: // to lower backward. data.Direction = 1; // to higher forward. break; case 7: // to lower no directional info. data.Direction = 3; // to higher no directional info. break; case 9: // no low/high forward. data.Direction = 10; // no low/high backward. break; case 10: // no low/high backward. data.Direction = 9; // no low/high forward. break; } return(data); }
public void TestCHEdgeDataSetDirection() { CHEdgeData edge = new CHEdgeData(); this.DoTestSetDirection(edge, false, false, false); this.DoTestSetDirection(edge, true, false, false); this.DoTestSetDirection(edge, false, true, false); this.DoTestSetDirection(edge, false, false, true); this.DoTestSetDirection(edge, true, true, false); this.DoTestSetDirection(edge, true, false, true); this.DoTestSetDirection(edge, false, true, true); this.DoTestSetDirection(edge, true, true, true); this.DoTestSetDirection(edge, false, false); this.DoTestSetDirection(edge, true, false); this.DoTestSetDirection(edge, false, true); this.DoTestSetDirection(edge, true, true); }
/// <summary> /// Adds all downward edges. /// </summary> /// <param name="graph"></param> public static void AddDownwardEdges(this IDynamicGraph <CHEdgeData> graph) { // add the reverse edges to get a easy depth-first search. for (uint vertexId = 1; vertexId < graph.VertexCount; vertexId++) { List <KeyValuePair <uint, CHEdgeData> > arcs = new List <KeyValuePair <uint, CHEdgeData> >(graph.GetArcs(vertexId)); foreach (KeyValuePair <uint, CHEdgeData> arc in arcs) { if (arc.Value.ToHigher) { // create severse edge. CHEdgeData reverseEdge = new CHEdgeData(); reverseEdge.SetDirection(arc.Value.Backward, arc.Value.Forward, false); reverseEdge.Weight = arc.Value.Weight; graph.AddArc(arc.Key, vertexId, reverseEdge, null); } } } }
/// <summary> /// Returns the exact inverse of this edge. /// </summary> /// <returns></returns> public IGraphEdgeData Reverse() { var reverse = new CHEdgeData(); // forward/backward specific info. reverse.BackwardWeight = this.ForwardWeight; reverse.BackwardContractedId = this.ForwardContractedId; reverse.ForwardContractedId = this.BackwardContractedId; reverse.ForwardWeight = this.BackwardWeight; // tags. reverse.Tags = this.Tags; reverse.TagsForward = !this.TagsForward; // contracted direction info. reverse._contractedDirection = this._contractedDirection; switch(_contractedDirection) { case 1: reverse._contractedDirection = 2; break; case 2: reverse._contractedDirection = 1; break; } return reverse; }
/// <summary> /// Contracts the given vertex. /// </summary> /// <param name="vertex"></param> public void Contract(uint vertex) { if (_contracted.Length > vertex && _contracted[vertex]) { throw new Exception("Is already contracted!"); } // keep the neighbours. HashSet<KeyValuePair<uint, CHEdgeData>> neighbours = new HashSet<KeyValuePair<uint, CHEdgeData>>(); // get all information from the source. KeyValuePair<uint, CHEdgeData>[] edges = _target.GetArcs(vertex); // remove all informative edges. edges = edges.RemoveInformativeEdges(); // report the before contraction event. this.OnBeforeContraction(vertex, edges); // remove the edges from the neighbours to the target. foreach (KeyValuePair<uint, CHEdgeData> edge in edges) { // remove the edge. _target.DeleteArc(edge.Key, vertex); // keep the neighbour. if (_keepDirectNeighbours && !edge.Value.HasContractedVertex) { // edge does represent a neighbour relation. neighbours.Add( new KeyValuePair<uint, CHEdgeData>(edge.Key, edge.Value.ConvertToInformative())); } } // loop over each combination of edges just once. for (int x = 1; x < edges.Length; x++) { // loop over all elements first. KeyValuePair<uint, CHEdgeData> xEdge = edges[x]; if (xEdge.Value.IsInformative) { continue; } for (int y = 0; y < x; y++) { // loop over all elements. KeyValuePair<uint, CHEdgeData> yEdge = edges[y]; if (yEdge.Value.IsInformative) { continue; } // calculate the total weight. float weight = xEdge.Value.Weight + yEdge.Value.Weight; // add the combinations of these edges. if (((xEdge.Value.Backward && yEdge.Value.Forward) || (yEdge.Value.Backward && xEdge.Value.Forward)) && (xEdge.Key != yEdge.Key)) { // there is a connection from x to y and there is no witness path. bool witnessXToY = _witnessCalculator.Exists(_target, xEdge.Key, yEdge.Key, vertex, weight, 100); bool witnessYToX = _witnessCalculator.Exists(_target, yEdge.Key, xEdge.Key, vertex, weight, 100); // create x-to-y data and edge. CHEdgeData dataXToY = new CHEdgeData(); bool forward = (xEdge.Value.Backward && yEdge.Value.Forward) && !witnessXToY; bool backward = (yEdge.Value.Backward && xEdge.Value.Forward) && !witnessYToX; dataXToY.SetDirection(forward, backward, true); dataXToY.Weight = weight; dataXToY.ContractedVertexId = vertex; if ((dataXToY.Forward || dataXToY.Backward) || !_target.HasArc(xEdge.Key, yEdge.Key)) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. _target.AddArc(xEdge.Key, yEdge.Key, dataXToY, _comparer); } // create y-to-x data and edge. CHEdgeData dataYToX = new CHEdgeData(); forward = (yEdge.Value.Backward && xEdge.Value.Forward) && !witnessYToX; backward = (xEdge.Value.Backward && yEdge.Value.Forward) && !witnessXToY; dataYToX.SetDirection(forward, backward, true); dataYToX.Weight = weight; dataYToX.ContractedVertexId = vertex; if ((dataYToX.Forward || dataYToX.Backward) || !_target.HasArc(yEdge.Key, xEdge.Key)) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. _target.AddArc(yEdge.Key, xEdge.Key, dataYToX, _comparer); } } } } // mark the vertex as contracted. this.MarkContracted(vertex); // notify a contracted neighbour. _calculator.NotifyContracted(vertex); // add contracted neighbour edges again. if (_keepDirectNeighbours) { foreach (KeyValuePair<uint, CHEdgeData> neighbour in neighbours) { _target.AddArc(neighbour.Key, vertex, neighbour.Value, null); } } // report the after contraction event. this.OnAfterContraction(vertex, edges); }
/// <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 (_contracted.Length > vertex && _contracted[vertex]) { throw new Exception("Is already contracted!"); } // keep the neighbours. HashSet <KeyValuePair <uint, CHEdgeData> > neighbours = new HashSet <KeyValuePair <uint, CHEdgeData> >(); // get all information from the source. KeyValuePair <uint, CHEdgeData>[] edges = _target.GetArcs(vertex); // remove all informative edges. edges = edges.RemoveInformativeEdges(); // report the before contraction event. this.OnBeforeContraction(vertex, edges); // remove the edges from the neighbours to the target. foreach (KeyValuePair <uint, CHEdgeData> edge in edges) { // remove the edge. _target.DeleteArc(edge.Key, vertex); // keep the neighbour. if (_keepDirectNeighbours && !edge.Value.HasContractedVertex) { // edge does represent a neighbour relation. neighbours.Add( new KeyValuePair <uint, CHEdgeData>(edge.Key, edge.Value.ConvertToInformative())); } } // loop over each combination of edges just once. for (int x = 1; x < edges.Length; x++) { // loop over all elements first. KeyValuePair <uint, CHEdgeData> xEdge = edges[x]; if (xEdge.Value.IsInformative) { continue; } for (int y = 0; y < x; y++) { // loop over all elements. KeyValuePair <uint, CHEdgeData> yEdge = edges[y]; if (yEdge.Value.IsInformative) { continue; } // calculate the total weight. float weight = xEdge.Value.Weight + yEdge.Value.Weight; // add the combinations of these edges. if (((xEdge.Value.Backward && yEdge.Value.Forward) || (yEdge.Value.Backward && xEdge.Value.Forward)) && (xEdge.Key != yEdge.Key)) { // there is a connection from x to y and there is no witness path. bool witnessXToY = _witnessCalculator.Exists(_target, xEdge.Key, yEdge.Key, vertex, weight, 100); bool witnessYToX = _witnessCalculator.Exists(_target, yEdge.Key, xEdge.Key, vertex, weight, 100); // create x-to-y data and edge. CHEdgeData dataXToY = new CHEdgeData(); bool forward = (xEdge.Value.Backward && yEdge.Value.Forward) && !witnessXToY; bool backward = (yEdge.Value.Backward && xEdge.Value.Forward) && !witnessYToX; dataXToY.SetDirection(forward, backward, true); dataXToY.Weight = weight; dataXToY.ContractedVertexId = vertex; if ((dataXToY.Forward || dataXToY.Backward) || !_target.HasArc(xEdge.Key, yEdge.Key)) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. _target.AddArc(xEdge.Key, yEdge.Key, dataXToY, _comparer); } // create y-to-x data and edge. CHEdgeData dataYToX = new CHEdgeData(); forward = (yEdge.Value.Backward && xEdge.Value.Forward) && !witnessYToX; backward = (xEdge.Value.Backward && yEdge.Value.Forward) && !witnessXToY; dataYToX.SetDirection(forward, backward, true); dataYToX.Weight = weight; dataYToX.ContractedVertexId = vertex; if ((dataYToX.Forward || dataYToX.Backward) || !_target.HasArc(yEdge.Key, xEdge.Key)) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. _target.AddArc(yEdge.Key, xEdge.Key, dataYToX, _comparer); } } } } // mark the vertex as contracted. this.MarkContracted(vertex); // notify a contracted neighbour. _calculator.NotifyContracted(vertex); // add contracted neighbour edges again. if (_keepDirectNeighbours) { foreach (KeyValuePair <uint, CHEdgeData> neighbour in neighbours) { _target.AddArc(neighbour.Key, vertex, neighbour.Value, null); } } // report the after contraction event. this.OnAfterContraction(vertex, edges); }
/// <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); // replace the adjacent edges with edges that are point up. var edgesForContractions = new List<Edge<CHEdgeData>>(edges.Count); var tos = new List<uint>(edges.Count); foreach (var edge in edges) { if (!edge.EdgeData.ToLower) { // the edge is not to lower or higher. // use this edge for contraction. edgesForContractions.Add(edge); tos.Add(edge.Neighbour); // overwrite the old edge making it point 'to higher' only. var toHigherData = edge.EdgeData; toHigherData.SetContractedDirection(true, false); ICoordinateCollection shape = null; if (!_target.GetEdgeShape(vertex, edge.Neighbour, out shape)) { shape = null; } _target.AddEdge(vertex, edge.Neighbour, toHigherData, shape); } } // loop over each combination of edges just once. int newEdge = 0; var witnesses = new bool[edgesForContractions.Count]; var tosWeights = new List<float>(edgesForContractions.Count); var toRequeue = new HashSet<uint>(); for (int x = 0; x < edgesForContractions.Count; x++) { // loop over all elements first. var xEdge = edgesForContractions[x]; if (!xEdge.EdgeData.Backward) { continue; } // calculate max weight. tosWeights.Clear(); for (int idx = 0; idx < edgesForContractions.Count; idx++) { // update maxWeight. var yEdge = edgesForContractions[idx]; if (xEdge.Neighbour != yEdge.Neighbour && yEdge.EdgeData.Forward) { // reset witnesses. float weight = (float)xEdge.EdgeData.BackwardWeight + (float)yEdge.EdgeData.ForwardWeight; witnesses[idx] = false; tosWeights.Add(weight); } else { // already set this to true, not use calculating it's witness. witnesses[idx] = true; tosWeights.Add(0); } } _contractionWitnessCalculator.Exists(_target, xEdge.Neighbour, tos, tosWeights, int.MaxValue, ref witnesses); for (int y = 0; y < edgesForContractions.Count; y++) { // loop over all elements. var yEdge = edgesForContractions[y]; // add the combinations of these edges. if (yEdge.EdgeData.Forward && 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 forward = (xEdge.EdgeData.Backward && yEdge.EdgeData.Forward) && !witnesses[y]; if (forward) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. // calculate the total weight. var forwardWeight = xEdge.EdgeData.BackwardWeight + yEdge.EdgeData.ForwardWeight; CHEdgeData data; if (_target.GetEdge(xEdge.Neighbour, yEdge.Neighbour, out data)) { // there already is an edge; evaluate for each direction. if (forward && data.ForwardWeight > forwardWeight) { // replace forward edge. ICoordinateCollection shape; if (data.RepresentsNeighbourRelations && _target.GetEdgeShape(xEdge.Neighbour, yEdge.Neighbour, out shape) && shape != null && shape.Count > 0) { // an edge that represents a relation between two neighbours and has shapes should never be replaced. // TODO: keep existing edge by inserting a dummy vertex for one of the shapes. // TODO: check if this is still needed because these case are supposed to be remove in osm->graph conversions. // WARNING: The assumption here is that the weight is in direct relation with the distance. var shapeCoordinates = new List<GeoCoordinateSimple>(shape.ToSimpleArray()); float latitude, longitude; _target.GetVertex(xEdge.Neighbour, out latitude, out longitude); var previousCoordinate = new GeoCoordinate(shapeCoordinates[0]); var distanceFirst = (new GeoCoordinate(latitude, longitude)).DistanceEstimate(previousCoordinate).Value; var totalDistance = distanceFirst; for(int idx = 1; idx < shapeCoordinates.Count; idx++) { var currentCoordinate = new GeoCoordinate(shapeCoordinates[idx]); totalDistance = totalDistance + currentCoordinate.DistanceEstimate(previousCoordinate).Value; previousCoordinate = currentCoordinate; } _target.GetVertex(yEdge.Neighbour, out latitude, out longitude); totalDistance = totalDistance + previousCoordinate.DistanceEstimate(new GeoCoordinate(latitude, longitude)).Value; // calculate the new edge data's. float firstPartRatio = (float)(distanceFirst / totalDistance); float secondPartRatio = 1 - firstPartRatio; // REMARK: the edge being split can never have contracted id's because it would not have a shape. var firstPartEdgeData = new CHEdgeData() { BackwardContractedId = data.BackwardContractedId, BackwardWeight = data.BackwardWeight, ForwardContractedId = data.ForwardContractedId, ForwardWeight = data.ForwardWeight, Tags = data.Tags, TagsForward = data.TagsForward }; var secondPartEdgeData = new CHEdgeData() { BackwardContractedId = data.BackwardContractedId, BackwardWeight = data.BackwardWeight, ForwardContractedId = data.ForwardContractedId, ForwardWeight = data.ForwardWeight, Tags = data.Tags, TagsForward = data.TagsForward }; // calculate firstpart weights. if(data.Backward) { firstPartEdgeData.BackwardWeight = firstPartEdgeData.BackwardWeight * firstPartRatio; secondPartEdgeData.BackwardWeight = secondPartEdgeData.BackwardWeight * secondPartRatio; } if (data.Forward) { firstPartEdgeData.ForwardWeight = firstPartEdgeData.ForwardWeight * firstPartRatio; secondPartEdgeData.ForwardWeight = secondPartEdgeData.ForwardWeight * secondPartRatio; } // add intermediate vertex. var newVertex = _target.AddVertex(shapeCoordinates[0].Latitude, shapeCoordinates[0].Longitude); toRequeue.Add(newVertex); // immidiately queue for contraction. // add edge before. _target.AddEdge(xEdge.Neighbour, newVertex, firstPartEdgeData, null); // add edge after. var secondPartShape = shapeCoordinates.GetRange(1, shapeCoordinates.Count - 1); _target.AddEdge(newVertex, yEdge.Neighbour, secondPartEdgeData, new CoordinateArrayCollection<GeoCoordinateSimple>(secondPartShape.ToArray())); // remove original edge. _target.RemoveEdge(xEdge.Neighbour, yEdge.Neighbour); } toRequeue.Add(xEdge.Neighbour); newEdge++; data.ForwardWeight = forwardWeight; data.ForwardContractedId = vertex; _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, data, null, _comparer); } } else { // there is no edge, just add the data. var dataXToY = new CHEdgeData(); dataXToY.BackwardWeight = float.MaxValue; dataXToY.SetContractedDirection(false, false); toRequeue.Add(xEdge.Neighbour); newEdge++; dataXToY.ForwardWeight = forwardWeight; dataXToY.ForwardContractedId = vertex; _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, dataXToY, null, _comparer); } } } } } // update priority of direct neighbours. foreach (var neighbour in toRequeue) { this.ReQueue(neighbour); } // mark the vertex as contracted. this.MarkContracted(vertex); // notify a contracted neighbour. _calculator.NotifyContracted(vertex); // report the after contraction event. this.OnAfterContraction(vertex, edges); }
/// <summary> /// Tests if the two given edges are compared correctly by the CHEdgeDataComparer. /// </summary> /// <param name="overapping"></param> /// <param name="overlappee"></param> private void DoTestCompare(CHEdgeData overapping, CHEdgeData overlappee, bool result) { CHEdgeDataComparer comparer = new CHEdgeDataComparer(); if (result) { Assert.IsTrue(comparer.Overlaps(overapping, overlappee)); } else { Assert.IsFalse(comparer.Overlaps(overapping, overlappee)); } }
/// <summary> /// Tests the set direction functionality for the given parameters. /// </summary> /// <param name="edge"></param> /// <param name="forward"></param> /// <param name="backward"></param> /// <param name="toHigher"></param> private void DoTestSetDirection(CHEdgeData edge, bool forward, bool backward, bool toHigher) { edge.SetDirection(forward, backward, toHigher); if (forward) { Assert.IsTrue(edge.Forward); } else { Assert.IsFalse(edge.Forward); } if (backward) { Assert.IsTrue(edge.Backward); } else { Assert.IsFalse(edge.Backward); } if (toHigher) { Assert.IsTrue(edge.ToHigher); } else { Assert.IsFalse(edge.ToHigher); } }
/// <summary> /// Contracts the given vertex. /// </summary> /// <param name="vertex"></param> public void Contract(uint vertex) { if (_contracted.Length > vertex && _contracted[vertex]) { throw new Exception("Is already contracted!"); } // keep the neighbours. var neighbours = new HashSet <KeyValuePair <uint, CHEdgeData> >(); // get all information from the source. var edges = _target.GetEdges(vertex); // report the before contraction event. this.OnBeforeContraction(vertex, edges); // replace the adjacent edges with edges that are point up. var edgesForContractions = new List <KeyValuePair <uint, CHEdgeData> >(edges.Length); foreach (var edge in edges) { if (!edge.Value.ToLower && !edge.Value.ToHigher) { // the edge is not to lower or higher. // use this edge for contraction. edgesForContractions.Add(edge); // overwrite the old edge making it point 'to higher' only. _target.AddEdge(vertex, edge.Key, new CHEdgeData(edge.Value.Weight, edge.Value.Forward, edge.Value.Backward, true, edge.Value.ContractedVertexId, edge.Value.Tags), null); } } // loop over each combination of edges just once. for (int x = 1; x < edgesForContractions.Count; x++) { // loop over all elements first. var xEdge = edgesForContractions[x]; for (int y = 0; y < x; y++) { // loop over all elements. var yEdge = edgesForContractions[y]; // calculate the total weight. var weight = xEdge.Value.Weight + yEdge.Value.Weight; // add the combinations of these edges. if (((xEdge.Value.Backward && yEdge.Value.Forward) || (yEdge.Value.Backward && xEdge.Value.Forward)) && (xEdge.Key != yEdge.Key)) { // there is a connection from x to y and there is no witness path. var witnessXToY = _contractionWitnessCalculator.Exists(_target, xEdge.Key, yEdge.Key, vertex, weight, int.MaxValue); var witnessYToX = _contractionWitnessCalculator.Exists(_target, yEdge.Key, xEdge.Key, vertex, weight, int.MaxValue); // create x-to-y data and edge. var dataXToY = new CHEdgeData(); var forward = (xEdge.Value.Backward && yEdge.Value.Forward) && !witnessXToY; var backward = (yEdge.Value.Backward && xEdge.Value.Forward) && !witnessYToX; if ((forward || backward) || !_target.ContainsEdge(xEdge.Key, yEdge.Key)) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. dataXToY.SetDirection(forward, backward); dataXToY.Weight = weight; dataXToY.ContractedVertexId = vertex; _target.AddEdge(xEdge.Key, yEdge.Key, dataXToY, null, _comparer); } } } } // mark the vertex as contracted. this.MarkContracted(vertex); // notify a contracted neighbour. _calculator.NotifyContracted(vertex); // report the after contraction event. this.OnAfterContraction(vertex, edges); }
/// <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 (_contracted.Length > vertex && _contracted[vertex]) { throw new Exception("Is already contracted!"); } // keep the neighbours. HashSet<uint> neighbours = new HashSet<uint>(); // get all information from the source. KeyValuePair<uint, CHEdgeData>[] edges = _target.GetArcs(vertex); // report the before contraction event. this.OnBeforeContraction(vertex, edges); // remove the edges from the neighbours to the target. foreach (KeyValuePair<uint, CHEdgeData> edge in edges) { // remove the edge. _target.DeleteArc(edge.Key, vertex); // keep the neighbour. neighbours.Add(edge.Key); } // loop over each combination of edges just once. for (int x = 1; x < edges.Length; x++) { // loop over all elements first. KeyValuePair<uint, CHEdgeData> x_edge = edges[x]; //System.Threading.Tasks.Parallel.For(0, x, y => for (int y = 0; y < x; y++) { // loop over all elements. KeyValuePair<uint, CHEdgeData> y_edge = edges[y]; // calculate the total weight. float weight = x_edge.Value.Weight + y_edge.Value.Weight; // add the combinations of these edges. if ((x_edge.Value.Backward && y_edge.Value.Forward) || (y_edge.Value.Backward && x_edge.Value.Forward)) { // there is a connection from x to y and there is no witness path. bool witness_x_to_y = _witness_calculator.Exists(x_edge.Key, y_edge.Key, vertex, weight, 100); bool witness_y_to_x = _witness_calculator.Exists(y_edge.Key, x_edge.Key, vertex, weight, 100); // create x-to-y data and edge. CHEdgeData data_x_to_y = new CHEdgeData(); data_x_to_y.Forward = (x_edge.Value.Backward && y_edge.Value.Forward) && !witness_x_to_y; data_x_to_y.Backward = (y_edge.Value.Backward && x_edge.Value.Forward) && !witness_y_to_x; data_x_to_y.Weight = weight; data_x_to_y.ContractedVertexId = vertex; if ((data_x_to_y.Forward || data_x_to_y.Backward) || !_target.HasNeighbour(x_edge.Key, y_edge.Key)) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. _target.AddArc(x_edge.Key, y_edge.Key, data_x_to_y, _comparer); } // create y-to-x data and edge. CHEdgeData data_y_to_x = new CHEdgeData(); data_y_to_x.Forward = (y_edge.Value.Backward && x_edge.Value.Forward) && !witness_y_to_x; data_y_to_x.Backward = (x_edge.Value.Backward && y_edge.Value.Forward) && !witness_x_to_y; data_y_to_x.Weight = weight; data_y_to_x.ContractedVertexId = vertex; if ((data_y_to_x.Forward || data_y_to_x.Backward) || !_target.HasNeighbour(y_edge.Key, x_edge.Key)) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. _target.AddArc(y_edge.Key, x_edge.Key, data_y_to_x, _comparer); } } } //}); } //// re-enqueue all the neigbours. //foreach (uint neighbour in neighbours) //{ // if (_queue.Remove(neighbour)) // { // this.ReQueue(neighbour); // } // if (_contracted.Length > neighbour && _contracted[neighbour]) // { // vertex was neighbour but already contracted (= impossible) // throw new Exception(string.Format("Vertex {0} has contracted neighbour: {1}!", // vertex, neighbour)); // } //} // mark the vertex as contracted. this.MarkContracted(vertex); // notify a contracted neighbour. _calculator.NotifyContracted(vertex); // report the after contraction event. this.OnAfterContraction(vertex, edges); }