예제 #1
0
        /// <summary>
        /// Search forward from one vertex.
        /// </summary>
        /// <returns></returns>
        private void SearchForward(BinaryHeap <EdgePath <T> > queue, EdgePath <T> current)
        {
            if (current != null)
            { // there is a next vertex found.
                // check restrictions.
                if (_restrictions != null &&
                    _restrictions.Update(current.Vertex) &&
                    _restrictions.Restricts(current.Vertex))
                { // vertex is restricted, don't settle.
                    return;
                }

                // get the edge enumerator.
                var edgeEnumerator = _graph.GetEdgeEnumerator();

                // add to the settled vertices.
                EdgePath <T> previousLinkedRoute;
                if (_forwardVisits.TryGetValue(current.Vertex, out previousLinkedRoute))
                {
                    if (_weightHandler.IsLargerThan(previousLinkedRoute.Weight, current.Weight))
                    { // settle the vertex again if it has a better weight.
                        _forwardVisits[current.Vertex] = current;
                    }
                }
                else
                { // settled the vertex.
                    _forwardVisits.Add(current.Vertex, current);
                }

                // get neighbours.
                edgeEnumerator.MoveTo(current.Vertex);

                // add the neighbours to the queue.
                while (edgeEnumerator.MoveNext())
                {
                    bool?neighbourDirection;
                    var  neighbourWeight = _weightHandler.GetEdgeWeight(edgeEnumerator.Current, out neighbourDirection);

                    if (neighbourDirection == null || neighbourDirection.Value)
                    { // the edge is forward, and is to higher or was not contracted at all.
                        var neighbourNeighbour = edgeEnumerator.Neighbour;
                        if (!_forwardVisits.ContainsKey(neighbourNeighbour))
                        { // if not yet settled.
                            var routeToNeighbour = new EdgePath <T>(
                                neighbourNeighbour, _weightHandler.Add(current.Weight, neighbourWeight), current);
                            queue.Push(routeToNeighbour, _weightHandler.GetMetric(routeToNeighbour.Weight));
                        }
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Contracts the given vertex.
        /// </summary>
        private void Contract(uint vertex)
        {
            // get and keep edges.
            var edges = new List <MetaEdge>(_graph.GetEdgeEnumerator(vertex));

            // remove 'downward' edge to vertex.
            var i = 0;

            while (i < edges.Count)
            {
                _graph.RemoveEdge(edges[i].Neighbour, vertex);

                if (_contractedFlags[edges[i].Neighbour])
                { // neighbour was already contracted, remove 'downward' edge and exclude it.
                    _graph.RemoveEdge(vertex, edges[i].Neighbour);
                    edges.RemoveAt(i);
                }
                else
                { // move to next edge.
                    i++;
                }
            }

            // check for a restriction, if vertex is restricted don't add shortcuts.
            if (_restrictions != null &&
                _restrictions.Update(vertex))
            {
                if (_restrictions.Restricts(vertex))
                {
                    return;
                }
            }

            // loop over all edge-pairs once.
            for (var j = 1; j < edges.Count; j++)
            {
                var edge1 = edges[j];

                bool?edge1Direction;
                var  edge1Weight          = _weightHandler.GetEdgeWeight(edge1, out edge1Direction);
                var  edge1CanMoveForward  = edge1Direction == null || edge1Direction.Value;
                var  edge1CanMoveBackward = edge1Direction == null || !edge1Direction.Value;

                // figure out what witness paths to calculate.
                var forwardWitnesses  = new bool[j];
                var backwardWitnesses = new bool[j];
                var targets           = new List <uint>(j);
                var targetWeights     = new List <T>(j);
                var targetMetrics     = new List <float>(j);
                for (var k = 0; k < j; k++)
                {
                    var edge2 = edges[k];

                    bool?edge2Direction;
                    var  edge2Weight          = _weightHandler.GetEdgeWeight(edge2, out edge2Direction);
                    var  edge2CanMoveForward  = edge2Direction == null || edge2Direction.Value;
                    var  edge2CanMoveBackward = edge2Direction == null || !edge2Direction.Value;

                    // use witness flags to represent impossible routes.
                    forwardWitnesses[k]  = !(edge1CanMoveBackward && edge2CanMoveForward);
                    backwardWitnesses[k] = !(edge1CanMoveForward && edge2CanMoveBackward);

                    targets.Add(edge2.Neighbour);
                    var totalWeight = _weightHandler.Add(edge1Weight, edge2Weight);
                    targetWeights.Add(totalWeight);
                    targetMetrics.Add(_weightHandler.GetMetric(totalWeight));
                }

                // calculate all witness paths.
                _witnessCalculator.Calculate(_graph.Graph, edge1.Neighbour, targets, targetMetrics, ref forwardWitnesses,
                                             ref backwardWitnesses, vertex);

                // add contracted edges if needed.
                for (var k = 0; k < j; k++)
                {
                    var edge2 = edges[k];

                    if (edge1.Neighbour == edge2.Neighbour)
                    { // do not try to add a shortcut between identical vertices.
                        continue;
                    }

                    if (!forwardWitnesses[k] && !backwardWitnesses[k])
                    { // add bidirectional edge.
                        _weightHandler.AddOrUpdateEdge(_graph, edge1.Neighbour, edge2.Neighbour,
                                                       vertex, null, targetWeights[k]);
                        //_graph.AddOrUpdateEdge(edge1.Neighbour, edge2.Neighbour,
                        //    targetWeights[k], null, vertex);
                        _weightHandler.AddOrUpdateEdge(_graph, edge2.Neighbour, edge1.Neighbour,
                                                       vertex, null, targetWeights[k]);
                        //_graph.AddOrUpdateEdge(edge2.Neighbour, edge1.Neighbour,
                        //    targetWeights[k], null, vertex);
                    }
                    else if (!forwardWitnesses[k])
                    { // add forward edge.
                        _weightHandler.AddOrUpdateEdge(_graph, edge1.Neighbour, edge2.Neighbour,
                                                       vertex, true, targetWeights[k]);
                        //_graph.AddOrUpdateEdge(edge1.Neighbour, edge2.Neighbour,
                        //    targetWeights[k], true, vertex);
                        _weightHandler.AddOrUpdateEdge(_graph, edge2.Neighbour, edge1.Neighbour,
                                                       vertex, false, targetWeights[k]);
                        //_graph.AddOrUpdateEdge(edge2.Neighbour, edge1.Neighbour,
                        //    targetWeights[k], false, vertex);
                    }
                    else if (!backwardWitnesses[k])
                    { // add forward edge.
                        _weightHandler.AddOrUpdateEdge(_graph, edge1.Neighbour, edge2.Neighbour,
                                                       vertex, false, targetWeights[k]);
                        //_graph.AddOrUpdateEdge(edge1.Neighbour, edge2.Neighbour,
                        //    targetWeights[k], false, vertex);
                        _weightHandler.AddOrUpdateEdge(_graph, edge2.Neighbour, edge1.Neighbour,
                                                       vertex, true, targetWeights[k]);
                        //_graph.AddOrUpdateEdge(edge2.Neighbour, edge1.Neighbour,
                        //    targetWeights[k], true, vertex);
                    }
                }
            }

            _contractedFlags[vertex] = true;
            _priorityCalculator.NotifyContracted(vertex);
        }