Generates a sequence of unique IDs.
Call GetNextID to get a unique integer ID. By default, generates a simple integer sequence that starts at 1.

This class can be used to generate unique IDs for graphs, vertices, and edges. The class responsible for creating objects of one of these types should define a static field of type IDGenerator, then call GetNextID every time an object of that type is created. This results in independent sequences for each type.

Inheritance: NodeXLBase
        protected void MergeCommunities
            (LinkedList<Community> oCommunities,CommunityPair oCommunityPairToMerge,DeltaQMaxHeap oDeltaQMaxHeap,Int32 iEdgesInGraph,IDGenerator oIDGenerator)
        {
            Debug.Assert(oCommunityPairToMerge != null);
            Debug.Assert(oCommunities != null);
            Debug.Assert(oDeltaQMaxHeap != null);
            Debug.Assert(iEdgesInGraph > 0);
            Debug.Assert(oIDGenerator != null);

            // Merge Community1 and Community2 into a NewCommunity.

            Community oCommunity1 = oCommunityPairToMerge.Community1;
            Community oCommunity2 = oCommunityPairToMerge.Community2;

            Community oNewCommunity = new Community();

            oNewCommunity.ID = oIDGenerator.GetNextID();
            oNewCommunity.Degree = oCommunity1.Degree + oCommunity2.Degree;

            ICollection<IVertex> oNewCommunityVertices = oNewCommunity.Vertices;

            foreach (IVertex oVertex in oCommunity1.Vertices)
            {
                oNewCommunityVertices.Add(oVertex);
            }

            foreach (IVertex oVertex in oCommunity2.Vertices)
            {
                oNewCommunityVertices.Add(oVertex);
            }

            // In the following sorted lists, the sort key is the ID of
            // CommunityPair.Community2 and the value is the CommunityPair.

            SortedList<Int32, CommunityPair> oCommunity1CommunityPairs =
                oCommunity1.CommunityPairs;

            SortedList<Int32, CommunityPair> oCommunity2CommunityPairs =
                oCommunity2.CommunityPairs;

            SortedList<Int32, CommunityPair> oNewCommunityCommunityPairs =
                oNewCommunity.CommunityPairs;

            Int32 iCommunity1CommunityPairs = oCommunity1CommunityPairs.Count;
            Int32 iCommunity2CommunityPairs = oCommunity2CommunityPairs.Count;

            IList<Int32> oCommunity1Keys = oCommunity1CommunityPairs.Keys;

            IList<CommunityPair> oCommunity1Values =
                oCommunity1CommunityPairs.Values;

            IList<Int32> oCommunity2Keys = oCommunity2CommunityPairs.Keys;

            IList<CommunityPair> oCommunity2Values =
                oCommunity2CommunityPairs.Values;

            // Step through the community pairs in oCommunity1 and oCommunity2.

            Int32 iCommunity1Index = 0;
            Int32 iCommunity2Index = 0;

            Single fMaximumDeltaQ = Single.MinValue;
            CommunityPair oCommunityPairWithMaximumDeltaQ = null;
            Single fTwoTimesEdgesInGraph = 2F * iEdgesInGraph;

            while (iCommunity1Index < iCommunity1CommunityPairs ||
                   iCommunity2Index < iCommunity2CommunityPairs)
            {
                Int32 iCommunity1OtherCommunityID =
                    (iCommunity1Index < iCommunity1CommunityPairs) ?
                    oCommunity1Keys[iCommunity1Index] : Int32.MaxValue;

                Int32 iCommunity2OtherCommunityID =
                    (iCommunity2Index < iCommunity2CommunityPairs) ?
                    oCommunity2Keys[iCommunity2Index] : Int32.MaxValue;

                CommunityPair oNewCommunityPair = new CommunityPair();
                oNewCommunityPair.Community1 = oNewCommunity;

                if (iCommunity1OtherCommunityID == oCommunity2.ID)
                {
                    // This is an internal connection eliminated by the merge.
                    // Skip it.

                    iCommunity1Index++;
                    continue;
                }
                else if (iCommunity2OtherCommunityID == oCommunity1.ID)
                {
                    // This is an internal connection eliminated by the merge.
                    // Skip it.

                    iCommunity2Index++;
                    continue;
                }
                else if (iCommunity1OtherCommunityID == iCommunity2OtherCommunityID)
                {
                    // The other community is connected to both commmunity 1 and
                    // community 2.
                    //
                    // This is equation 10a from the paper "Finding Community
                    // Structure in Very Large Networks," by Clauset, Newman, and
                    // Moore.

                    oNewCommunityPair.Community2 =
                        oCommunity1Values[iCommunity1Index].Community2;

                    oNewCommunityPair.DeltaQ =
                        oCommunity1Values[iCommunity1Index].DeltaQ +
                        oCommunity2Values[iCommunity2Index].DeltaQ;

                    iCommunity1Index++;
                    iCommunity2Index++;
                }
                else if (iCommunity1OtherCommunityID < iCommunity2OtherCommunityID)
                {
                    // The other community is connected only to commmunity 1.
                    //
                    // This is equation 10b from the same paper.

                    Community oOtherCommunity =
                        oCommunity1Values[iCommunity1Index].Community2;

                    oNewCommunityPair.Community2 = oOtherCommunity;

                    Single fAj = oCommunity2.Degree / fTwoTimesEdgesInGraph;
                    Single fAk = oOtherCommunity.Degree / fTwoTimesEdgesInGraph;

                    oNewCommunityPair.DeltaQ =
                        oCommunity1Values[iCommunity1Index].DeltaQ -
                        2F * fAj * fAk;

                    iCommunity1Index++;
                }
                else
                {
                    // The other community is connected only to commmunity 2.
                    //
                    // This is equation 10c from the same paper.

                    Community oOtherCommunity =
                        oCommunity2Values[iCommunity2Index].Community2;

                    oNewCommunityPair.Community2 = oOtherCommunity;

                    Single fAi = oCommunity1.Degree / fTwoTimesEdgesInGraph;
                    Single fAk = oOtherCommunity.Degree / fTwoTimesEdgesInGraph;

                    oNewCommunityPair.DeltaQ =
                        oCommunity2Values[iCommunity2Index].DeltaQ -
                        2F * fAi * fAk;

                    iCommunity2Index++;
                }

                oNewCommunityCommunityPairs.Add(oNewCommunityPair.Community2.ID,
                    oNewCommunityPair);

                Single fNewCommunityPairDeltaQ = oNewCommunityPair.DeltaQ;

                if (fNewCommunityPairDeltaQ > fMaximumDeltaQ)
                {
                    fMaximumDeltaQ = oNewCommunityPair.DeltaQ;
                    oCommunityPairWithMaximumDeltaQ = oNewCommunityPair;
                }

                // The other community is connected to one or both of the merged
                // communities.  Update it.

                oNewCommunityPair.Community2.OnMergedCommunities(
                    oCommunity1, oCommunity2, oNewCommunity,
                    fNewCommunityPairDeltaQ, oDeltaQMaxHeap);
            }

            oNewCommunity.CommunityPairWithMaximumDeltaQ =
                oCommunityPairWithMaximumDeltaQ;

            // Update the community list.

            oCommunities.Remove(oCommunity1);
            oCommunities.Remove(oCommunity2);
            oCommunities.AddLast(oNewCommunity);

            // Update the max heap.

            oDeltaQMaxHeap.Remove(oCommunity1);
            oDeltaQMaxHeap.Remove(oCommunity2);
            oDeltaQMaxHeap.Add(oNewCommunity, oNewCommunity.MaximumDeltaQ);
        }
        public Boolean TryCalculateClustersWakitaTsurumi
            (IGraph oGraph, BackgroundWorker oBackgroundWorker,out ICollection<Community> oGraphMetrics)
        {
            Debug.Assert(oGraph != null);
            

            IVertexCollection oVertices = oGraph.Vertices;
            Int32 iVertices = oVertices.Count;
            Int32 iEdges = oGraph.Edges.Count;

            IDGenerator oIDGenerator = new IDGenerator(1);

            // Create and populate a community for each of the graph's vertices.

            LinkedList<Community> oCommunities =
                CreateCommunities(oVertices, oIDGenerator);

            oGraphMetrics = oCommunities;

            if (iVertices == 0 || iEdges == 0)
            {
                // There is no point in going any further.

                return (true);
            }

            // This max heap is used to keep track of the maximum delta Q value in
            // each community.  There is an element in the max heap for each
            // community.  The key is the Community and the value is the
            // Community's maximum delta Q.

            DeltaQMaxHeap oDeltaQMaxHeap = new DeltaQMaxHeap(iVertices);

            // Initialize all the delta Q values.

            InitializeDeltaQs(oCommunities, oDeltaQMaxHeap, iEdges);

            // Run the algorithm outlined in the Wakita/Tsurumi paper.

            BinaryHeapItem<Community, Single> oBinaryHeapItemWithMaximumDeltaQ;
            Int32 iMergeCycles = 0;

            // Retrieve the community pair with the largest delta Q.

            while (oDeltaQMaxHeap.TryGetTop(out oBinaryHeapItemWithMaximumDeltaQ))
            {
                // Check for cancellation and report progress every
                // MergeCyclesPerProgressReport calculations.

                if (
                    iMergeCycles % MergeCyclesPerProgressReport == 0
                    &&
                    !ReportProgressAndCheckCancellationPending(
                        iMergeCycles, iVertices, oBackgroundWorker)
                    )
                {
                    return (false);
                }

                Community oCommunityWithMaximumDeltaQ =
                    oBinaryHeapItemWithMaximumDeltaQ.Key;

                Single fMaximumGlobalDeltaQ =
                    oBinaryHeapItemWithMaximumDeltaQ.Value;

                if (fMaximumGlobalDeltaQ < 0)
                {
                    // Merging additional communities would yield worse results, so
                    // quit.

                    break;
                }

                // Merge the communities in the community pair with maximum
                // delta Q, update the maximum delta Q values for all communities,
                // and update the global max heap.

                CommunityPair oCommunityPairWithMaximumDeltaQ =
                    oCommunityWithMaximumDeltaQ.CommunityPairWithMaximumDeltaQ;

                MergeCommunities(oCommunities, oCommunityPairWithMaximumDeltaQ,
                    oDeltaQMaxHeap, iEdges, oIDGenerator);

                iMergeCycles++;
            }

            return (true);
        }
Esempio n. 3
0
    //*************************************************************************
    //  Constructor: Edge()
    //
    /// <overloads>
    /// Static constructor for the <see cref="Edge" /> class.
    /// </overloads>
    //*************************************************************************

    static Edge()
    {
        m_oIDGenerator = new IDGenerator();
    }
        protected LinkedList<Community> CreateCommunities
            (IVertexCollection oVertices, IDGenerator oIDGenerator)
        {
            Debug.Assert(oVertices != null);
            Debug.Assert(oIDGenerator != null);

            // This is the list of communities.  Initially, there will be one
            // community for each of the graph's vertices.

            LinkedList<Community> oCommunities = new LinkedList<Community>();

            // This temporary dictionary is used to map a vertex ID to a community.
            // The key is the IVertex.ID and the value is the corresponding
            // Community object.

            Dictionary<Int32, Community> oVertexIDDictionary =
                new Dictionary<Int32, Community>(oVertices.Count);

            // First, create a community for each of the graph's vertices.  Each
            // community contains just the vertex.

            foreach (IVertex oVertex in oVertices)
            {
                Community oCommunity = new Community();

                Int32 iID = oIDGenerator.GetNextID();

                oCommunity.ID = iID;
                oCommunity.Vertices.Add(oVertex);

                // TODO: IVertex.AdjacentVertices includes self-loops.  Should
                // self-loops be eliminated everywhere, including here and within
                // the graph's total edge count?  Not sure how self-loops are
                // affecting the algorithm used by this class...

                oCommunity.Degree = oVertex.AdjacentVertices.Count;

                oCommunities.AddLast(oCommunity);
                oVertexIDDictionary.Add(oVertex.ID, oCommunity);
            }

            // Now populate each community's list of community pairs.

            foreach (Community oCommunity1 in oCommunities)
            {
                Debug.Assert(oCommunity1.Vertices.Count == 1);

                IVertex oVertex = oCommunity1.Vertices.First();

                SortedList<Int32, CommunityPair> oCommunityPairs =
                    oCommunity1.CommunityPairs;

                foreach (IVertex oAdjacentVertex in oVertex.AdjacentVertices)
                {
                    if (oAdjacentVertex == oVertex)
                    {
                        // Skip self-loops.

                        continue;
                    }

                    Community oCommunity2 =
                        oVertexIDDictionary[oAdjacentVertex.ID];

                    CommunityPair oCommunityPair = new CommunityPair();
                    oCommunityPair.Community1 = oCommunity1;
                    oCommunityPair.Community2 = oCommunity2;

                    oCommunityPairs.Add(oCommunity2.ID, oCommunityPair);
                }
            }

            return (oCommunities);
        }
    TestGetNextID4()
    {
        // Use the second constructor.

        const Int32 FirstID = 100000;

        m_oIDGenerator = new IDGenerator(FirstID);

        for (Int32 i = FirstID; i < FirstID + 100; i++)
        {
            Assert.AreEqual( i, m_oIDGenerator.GetNextID() );
        }
    }
 SetUp()
 {
     m_oIDGenerator = new IDGenerator();
 }
Esempio n. 7
0
    //*************************************************************************
    //  Constructor: Graph()
    //
    /// <overloads>
    /// Static constructor for the Graph class.
    /// </overloads>
    //*************************************************************************

    static Graph()
    {
        m_oIDGenerator = new IDGenerator();
    }
Esempio n. 8
0
    //*************************************************************************
    //  Constructor: Vertex()
    //
    /// <overloads>
    /// Static constructor for the <see cref="Vertex" /> class.
    /// </overloads>
    //*************************************************************************

    static Vertex()
    {
        m_oIDGenerator = new IDGenerator();
    }
Esempio n. 9
0
        //*************************************************************************
        //  Constructor: Graph()
        //
        /// <overloads>
        /// Static constructor for the Graph class.
        /// </overloads>
        //*************************************************************************

        static Graph()
        {
            m_oIDGenerator = new IDGenerator();
        }
    TestGetNextIDBad2()
    {
        // Use the second constructor with an argument that will overflow.

        const Int32 FirstID = Int32.MaxValue - 1;

        m_oIDGenerator = new IDGenerator(FirstID);

        Assert.AreEqual( FirstID, m_oIDGenerator.GetNextID() );

        try
        {
            m_oIDGenerator.GetNextID();
        }
        catch (ApplicationException oApplicationException)
        {
            Assert.AreEqual(

                "Smrf.NodeXL.Core."
                + "IDGenerator.GetNextID: The maximum ID (2147483647) has"
                + " been reached."
                ,
                oApplicationException.Message
                );

            throw oApplicationException;
        }
    }
    TestGetNextIDBad()
    {
        // Use the second constructor with an invalid argument.

        const Int32 FirstID = Int32.MaxValue;

        try
        {
            m_oIDGenerator = new IDGenerator(FirstID);
        }
        catch (ArgumentException oArgumentException)
        {
            Assert.AreEqual(

                "Smrf.NodeXL.Core."
                + "IDGenerator.Constructor: The first ID can't be"
                + " Int32.MaxValue (2147483647).\r\n"
                + "Parameter name: firstID"
                ,
                oArgumentException.Message
                );

            throw oArgumentException;
        }
    }