QuikGraph icon indicating copy to clipboard operation
QuikGraph copied to clipboard

Properly implement the Centrality Approximation algorithm

Open KeRNeLith opened this issue 4 years ago • 1 comments

Implement the Centrality Approximation algorithm and unit test it.

The library had a draft of implementation for this algorithm, here is the basis:

using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using QuikGraph.Algorithms.Observers;
using QuikGraph.Algorithms.ShortestPath;
#if SUPPORTS_CRYPTO_RANDOM
using QuikGraph.Utils;
#endif

namespace QuikGraph.Algorithms
{
    /// <summary>
    /// Algorithm that computes the most important vertices in a graph.
    /// </summary>
    /// <typeparam name="TVertex">Vertex type.</typeparam>
    /// <typeparam name="TEdge">Edge type.</typeparam>
#if SUPPORTS_SERIALIZATION
    [Serializable]
#endif
    public sealed class CentralityApproximationAlgorithm<TVertex, TEdge> : AlgorithmBase<IVertexListGraph<TVertex, TEdge>>, IDisposable
        where TEdge : IEdge<TVertex>
    {
        [NotNull]
        private readonly DijkstraShortestPathAlgorithm<TVertex, TEdge> _dijkstra;

        [NotNull]
        private readonly IDisposable _predecessorRecorderSubscription;

        [NotNull]
        private readonly IDictionary<TVertex, double> _centralities = new Dictionary<TVertex, double>();

        /// <summary>
        /// Initializes a new instance of the <see cref="CentralityApproximationAlgorithm{TVertex,TEdge}"/> class.
        /// </summary>
        /// <param name="visitedGraph">Graph to visit.</param>
        /// <param name="distances">Function to compute the distance given an edge.</param>
        public CentralityApproximationAlgorithm(
            [NotNull] IVertexListGraph<TVertex, TEdge> visitedGraph,
            [NotNull] Func<TEdge, double> distances)
            : base(visitedGraph)
        {
            _dijkstra = new DijkstraShortestPathAlgorithm<TVertex, TEdge>(
                VisitedGraph,
                distances,
                DistanceRelaxers.ShortestDistance);
            var predecessorRecorder = new VertexPredecessorRecorderObserver<TVertex, TEdge>();
            _predecessorRecorderSubscription = predecessorRecorder.Attach(_dijkstra);
        }

        /// <summary>
        /// Function to gets the distance of a given edge.
        /// </summary>
        public Func<TEdge, double> Distances => _dijkstra.Weights;

        [NotNull]
        private Random _random =
#if SUPPORTS_CRYPTO_RANDOM
            new CryptoRandom();
#else
            new Random();
#endif

        /// <summary>
        /// Gets or sets the random number generator.
        /// </summary>
        [NotNull]
        public Random Rand
        {
            get => _random;
            set => _random = value ?? throw new ArgumentNullException(nameof(value), "Random number generator cannot be null.");
        }

        private int _maxIterations = 50;

        /// <summary>
        /// Maximum number of iterations.
        /// </summary>
        public int MaxIterationCount
        {
            get => _maxIterations;
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException(nameof(value), "Must be positive.");
                _maxIterations = value;
            }
        }

        #region AlgorithmBase<TGraph>

        /// <inheritdoc />
        protected override void Initialize()
        {
            base.Initialize();
            _centralities.Clear();
            foreach (TVertex vertex in VisitedGraph.Vertices)
                _centralities.Add(vertex, 0);
        }

        /// <inheritdoc />
        protected override void InternalCompute()
        {
            if (VisitedGraph.VertexCount == 0)
                return;

            // Compute temporary values
            int n = VisitedGraph.VertexCount;
            for (int i = 0; i < MaxIterationCount; ++i)
            {
                TVertex vertex = RandomGraphFactory.GetVertex(VisitedGraph, Rand);
                _dijkstra.Compute(vertex);

                foreach (TVertex u in VisitedGraph.Vertices)
                {
                    if (_dijkstra.TryGetDistance(u, out double d))
                        _centralities[u] += n * d / (MaxIterationCount * (n - 1));
                }
            }

            // Update
            foreach (TVertex vertex in _centralities.Keys.ToArray())
                _centralities[vertex] = 1.0 / _centralities[vertex];
        }

        #endregion

        #region IDisposable

        /// <inheritdoc />
        public void Dispose()
        {
            _predecessorRecorderSubscription.Dispose();
        }

        #endregion
    }
}

KeRNeLith avatar Feb 08 '20 15:02 KeRNeLith