diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26867dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.class +*.swp +*.swo +build/ +build.xml +manifest.mf +nbproject/ +test/ +src/PublicationClustering.java +/dist/ +test_network.ser +test* +*.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..79f4163 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Centre for Science and Technology Studies (CWTS), Leiden University + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ed7042 --- /dev/null +++ b/README.md @@ -0,0 +1,165 @@ +## Introduction + +This package provides data structures and algorithms for network analysis in `java`. +Currently, the focus of the package is restricted to clustering (or community detection) in networks. +In particular, the package contains an implementation of the [Leiden algorithm](https://arxiv.org/abs/xxx.xxxx) and the [Louvain algorithm](https://arxiv.org/abs/0803.0476). +Only undirected networks are supported. + +## Usage + +To run the clustering algorithms, the command-line tool `RunNetworkClustering` is provided. +The latest version of the tool is available as a pre-compiled `jar` file in the GitHub [release](https://github.com/CWTSLeiden/networkanalysis/releases/latest). +The source code is also available in this repository. +You can use it to [compile](#compilation) the code yourself. +The `.jar` file can be executed as follows: + +``` +java -jar RunNetworkClustering.jar +``` + +If no further arguments are provided, the following usage notice will be displayed: + +``` +Usage: RunNetworkClustering [options] + +Identify clusters (also known as communities) in a network, using either the +Leiden or the Louvain algorithm. + +The file in is expected to contain a tab-separated edge list +(without a header line). Nodes are represented by zero-index integer numbers. +Only undirected networks are supported. Each edge should be included only once +in the file. + +Options: +-q --quality-function {CPM|modularity} (default: CPM) + Quality function to be optimized. Either the CPM (constant Potts model) or + the modularity quality function can be used. +-r --resolution (default: 1.0) + Resolution parameter of the quality function. +-a --algorithm {Leiden|Louvain} (default: Leiden) + Algorithm for optimizing the quality function. Either the Leiden or the + Louvain algorithm can be used. +-s --random-starts (default: 1) + Number of random starts of the algorithm. +-i --iterations (default: 10) + Number of iterations of the algorithm. +--randomness (default: 0.01) + Randomness parameter of the Leiden algorithm. +--seed (default: random) + Seed of the random number generator. +-w --weighted-edges + Indicates that the edge list file has a third column containing edge + weights. +--sorted-edge-list + Indicates that the edge list file is sorted. The file should be sorted based + on the nodes in the first column, followed by the nodes in the second + column. Each edge should be included in both directions in the file. +--input-clustering (default: singleton clustering) + Read the initial clustering from the specified file. The file is expected to + contain two tab-separated columns (without a header line), first a column of + nodes and then a column of clusters. Nodes and clusters are both represented + by zero-index integer numbers. If no file is specified, a singleton + clustering (in which each node has its own cluster) is used as the initial + clustering. +-o --output-clustering (default: standard output) + Write the final clustering to the specified file. If no file is specified, + the standard output is used. +``` + +To run the clustering algorithms, you need `java 1.8.0` or higher. + +### Example + +The following example illustrates the use of the `RunNetworkClustering` tool. +Consider this network: + +```text + 0-----1 + \ / + \ / + 2 + | + 3 + / \ + / \ + 4-----5 +``` + +The network is encoded as an edge list that is saved in a tab-separated text file: + +```text +0 1 +1 2 +2 0 +2 3 +3 5 +5 4 +4 3 +``` + +Nodes must be represented by integer numbers starting from 0. +Assuming that the edge list has been saved in the file `network.txt`, the `RunNetworkClustering` tool can be run as follows: + +``` +java -jar RunNetworkClustering.jar -r 0.2 -o clusters.txt network.txt +``` + +In this case, clusters are identified using the Leiden algorithm based on the CPM quality function with a value of `0.2` for the resolution parameter. +The resulting clustering is saved in the text file `clusters.txt`: + +```text +0 +0 +0 +1 +1 +1 +``` + +The file `clusters.txt` shows that two clusters have been identified. +Cluster 0 includes nodes 0, 1, and 2. +Cluster 1 includes nodes 3, 4, and 5. +In the above example, the edges in the file `network.txt` have not been sorted. +To provide a sorted edge list as input, include the edges in both directions and use the option ``--sorted-edge-list``. +Furthermore, edge weights can be provided by adding a third column to the file `network.txt` and by using the option ``--weighted-edges``. + +## Compilation + +The source code can be compiled as follows: + +``` +javac -d build src/cwts/networkanalysis/*.java src/cwts/networkanalysis/run/*.java src/cwts/util/*.java +``` + +The compiled `class` files will be output to the directory `build`. +There are no external dependencies. +The `main` method is provided in the class `cwts.networkanalysis.run.RunNetworkClustering`. +After the code has been compiled, the `RunNetworkClustering` tool can be run as follows: + +``` +java -cp build cwts.networkanalysis.run.RunNetworkClustering +``` + +The latest stable version of the code is available from the [`master`](https://github.com/CWTSLeiden/networkanalysis/tree/master) branch on GitHub. +The most recent code, which may be under development, is available from the [`develop`](https://github.com/CWTSLeiden/networkanalysis/tree/develop) branch. + +## Issues + +If you encounter any issues, please report them using the [issue tracker](https://github.com/CWTSLeiden/networkanalysis/issues). +Before submitting, please examine whether issues have not yet been reported before. + +## Documentation + +Documentation of the source code is provided in the code in `javadoc` format. +The documentation is also available in a [compiled format](https://CWTSLeiden.github.io/networkanalysis). + +## Contribution + +You are welcome to contribute to this package. +Please follow the typical GitHub workflow: fork from this repository and make a pull request to submit your changes. +At the moment, we have not yet set up any continuous integration, so please make sure that any proposed pull request compiles and functions correctly. + +## License + +This package is distributed under the MIT License. +Please refer to the [`LICENSE`](LICENSE) file for further details. diff --git a/docs/allclasses-frame.html b/docs/allclasses-frame.html new file mode 100644 index 0000000..a2d4a89 --- /dev/null +++ b/docs/allclasses-frame.html @@ -0,0 +1,37 @@ + + + + + +All Classes + + + + + + +

All Classes

+ + + diff --git a/docs/allclasses-noframe.html b/docs/allclasses-noframe.html new file mode 100644 index 0000000..9c93011 --- /dev/null +++ b/docs/allclasses-noframe.html @@ -0,0 +1,37 @@ + + + + + +All Classes + + + + + + +

All Classes

+ + + diff --git a/docs/constant-values.html b/docs/constant-values.html new file mode 100644 index 0000000..5b93ec6 --- /dev/null +++ b/docs/constant-values.html @@ -0,0 +1,275 @@ + + + + + +Constant Field Values + + + + + + + + +
+ + + + +
+ +
+

Constant Field Values

+

Contents

+ +
+
+ + +

cwts.networkanalysis.*

+ + + + +

cwts.util.*

+ +
+ + + + + + diff --git a/docs/cwts/networkanalysis/CPMClusteringAlgorithm.html b/docs/cwts/networkanalysis/CPMClusteringAlgorithm.html new file mode 100644 index 0000000..d462c58 --- /dev/null +++ b/docs/cwts/networkanalysis/CPMClusteringAlgorithm.html @@ -0,0 +1,478 @@ + + + + + +CPMClusteringAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Class CPMClusteringAlgorithm

+
+
+
    +
  • java.lang.Object
  • +
  • +
      +
    • cwts.networkanalysis.CPMClusteringAlgorithm
    • +
    +
  • +
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Field Summary

      + + + + + + + + + + + + + + +
      Fields 
      Modifier and TypeField and Description
      static doubleDEFAULT_RESOLUTION +
      Default resolution parameter.
      +
      protected doubleresolution +
      Resolution parameter.
      +
      +
    • +
    + +
      +
    • + + +

      Constructor Summary

      + + + + + + + + + + + +
      Constructors 
      Constructor and Description
      CPMClusteringAlgorithm() +
      Constructs a CPM clustering algorithm.
      +
      CPMClusteringAlgorithm(double resolution) +
      Constructs a CPM clustering algorithm with a specified resolution + parameter.
      +
      +
    • +
    + + +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        DEFAULT_RESOLUTION

        +
        public static final double DEFAULT_RESOLUTION
        +
        Default resolution parameter.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        resolution

        +
        protected double resolution
        +
        Resolution parameter.
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        CPMClusteringAlgorithm

        +
        public CPMClusteringAlgorithm()
        +
        Constructs a CPM clustering algorithm.
        +
      • +
      + + + +
        +
      • +

        CPMClusteringAlgorithm

        +
        public CPMClusteringAlgorithm(double resolution)
        +
        Constructs a CPM clustering algorithm with a specified resolution + parameter.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        clone

        +
        public CPMClusteringAlgorithm clone()
        +
        Clones the algorithm.
        +
        +
        Overrides:
        +
        clone in class java.lang.Object
        +
        Returns:
        +
        Cloned algorithm
        +
        +
      • +
      + + + +
        +
      • +

        getResolution

        +
        public double getResolution()
        +
        Returns the resolution parameter.
        +
        +
        Returns:
        +
        Resolution parameter
        +
        +
      • +
      + + + +
        +
      • +

        setResolution

        +
        public void setResolution(double resolution)
        +
        Sets the resolution parameter.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        +
      • +
      + + + +
        +
      • +

        calcQuality

        +
        public double calcQuality(Network network,
        +                          Clustering clustering)
        +
        Calculates the quality of a clustering using the CPM quality function. + +

        + The CPM quality function is given by +

        + +
        + 1 / (2 * m) * sum(d(c[i], c[j]) * (a[i][j] - resolution * n[i] * + n[j])), +
        + +

        + where a[i][j] is the weight of the edge between nodes i + and j, n[i] is the weight of node i, m + is the total edge weight, and resolution is the resolution + parameter. The function d(c[i], c[j]) equals 1 if nodes + i and j belong to the same cluster and 0 otherwise. The sum is + taken over all pairs of nodes i and j. +

        + +

        + Modularity can be expressed in terms of CPM by setting n[i] + equal to the total weight of the edges between node i and its + neighbors and by rescaling the resolution parameter by 2 * m. +

        +
        +
        Specified by:
        +
        calcQuality in interface QualityClusteringAlgorithm
        +
        Parameters:
        +
        network - Network
        +
        clustering - Clustering
        +
        Returns:
        +
        Quality of the clustering
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/Clustering.html b/docs/cwts/networkanalysis/Clustering.html new file mode 100644 index 0000000..8604f7a --- /dev/null +++ b/docs/cwts/networkanalysis/Clustering.html @@ -0,0 +1,750 @@ + + + + + +Clustering + + + + + + + + +
+ + + + +
+ + +
+ +

Class Clustering

+
+
+
    +
  • java.lang.Object
  • +
  • +
      +
    • cwts.networkanalysis.Clustering
    • +
    +
  • +
+
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    java.io.Serializable, java.lang.Cloneable
    +
    +
    +
    +
    public class Clustering
    +extends java.lang.Object
    +implements java.lang.Cloneable, java.io.Serializable
    +
    Clustering of the nodes in a network. + +

    + Each node belongs to exactly one cluster. +

    +
    +
    See Also:
    +
    Serialized Form
    +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Summary

      + + + + + + + + + + + + + + + + + + +
      Fields 
      Modifier and TypeField and Description
      protected int[]clusters +
      Cluster of each node.
      +
      protected intnClusters +
      Number of clusters.
      +
      protected intnNodes +
      Number of nodes.
      +
      +
    • +
    + +
      +
    • + + +

      Constructor Summary

      + + + + + + + + + + + +
      Constructors 
      Constructor and Description
      Clustering(int nNodes) +
      Constructs a singleton clustering for a specified number of nodes.
      +
      Clustering(int[] clusters) +
      Constructs a clustering using a specified cluster for each node.
      +
      +
    • +
    + + +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        clusters

        +
        protected int[] clusters
        +
        Cluster of each node.
        +
      • +
      + + + +
        +
      • +

        nClusters

        +
        protected int nClusters
        +
        Number of clusters.
        +
      • +
      + + + +
        +
      • +

        nNodes

        +
        protected int nNodes
        +
        Number of nodes.
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        Clustering

        +
        public Clustering(int nNodes)
        +
        Constructs a singleton clustering for a specified number of nodes.
        +
        +
        Parameters:
        +
        nNodes - Number of nodes
        +
        +
      • +
      + + + +
        +
      • +

        Clustering

        +
        public Clustering(int[] clusters)
        +
        Constructs a clustering using a specified cluster for each node.
        +
        +
        Parameters:
        +
        clusters - Cluster of each node
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        load

        +
        public static Clustering load(java.lang.String filename)
        +                       throws java.lang.ClassNotFoundException,
        +                              java.io.IOException
        +
        Loads a clustering from a file.
        +
        +
        Parameters:
        +
        filename - File from which a clustering is loaded
        +
        Returns:
        +
        Loaded clustering
        +
        Throws:
        +
        java.lang.ClassNotFoundException - Class not found
        +
        java.io.IOException - Could not read the file
        +
        See Also:
        +
        save(String filename)
        +
        +
      • +
      + + + +
        +
      • +

        clone

        +
        public Clustering clone()
        +
        Clones the clustering.
        +
        +
        Overrides:
        +
        clone in class java.lang.Object
        +
        Returns:
        +
        Cloned clustering
        +
        +
      • +
      + + + +
        +
      • +

        save

        +
        public void save(java.lang.String filename)
        +          throws java.io.IOException
        +
        Saves the clustering in a file.
        +
        +
        Parameters:
        +
        filename - File in which the clustering is saved
        +
        Throws:
        +
        java.io.IOException - Could not write to the file
        +
        See Also:
        +
        load(String filename)
        +
        +
      • +
      + + + +
        +
      • +

        getNNodes

        +
        public int getNNodes()
        +
        Returns the number of nodes.
        +
        +
        Returns:
        +
        Number of nodes
        +
        +
      • +
      + + + +
        +
      • +

        getNClusters

        +
        public int getNClusters()
        +
        Returns the number of clusters.
        +
        +
        Returns:
        +
        Number of clusters
        +
        +
      • +
      + + + +
        +
      • +

        getClusters

        +
        public int[] getClusters()
        +
        Returns the cluster of each node.
        +
        +
        Returns:
        +
        Cluster of each node
        +
        +
      • +
      + + + +
        +
      • +

        getCluster

        +
        public int getCluster(int node)
        +
        Returns the cluster of a node.
        +
        +
        Parameters:
        +
        node - Node
        +
        Returns:
        +
        Cluster
        +
        +
      • +
      + + + +
        +
      • +

        getNNodesPerCluster

        +
        public int[] getNNodesPerCluster()
        +
        Returns the number of nodes per cluster.
        +
        +
        Returns:
        +
        Number of nodes per cluster
        +
        +
      • +
      + + + +
        +
      • +

        getNodesPerCluster

        +
        public int[][] getNodesPerCluster()
        +
        Returns a list of nodes per cluster.
        +
        +
        Returns:
        +
        List of nodes per cluster
        +
        +
      • +
      + + + +
        +
      • +

        setCluster

        +
        public void setCluster(int node,
        +                       int cluster)
        +
        Assigns a node to a cluster.
        +
        +
        Parameters:
        +
        node - Node
        +
        cluster - Cluster
        +
        +
      • +
      + + + +
        +
      • +

        initSingletonClusters

        +
        public void initSingletonClusters()
        +
        Initializes a singleton clustering. + +

        + Each node i is assigned to a cluster i. +

        +
      • +
      + + + +
        +
      • +

        removeEmptyClusters

        +
        public void removeEmptyClusters()
        +
        Removes empty clusters. + +

        + Clusters are relabeled to follow a strictly consecutive numbering + 0, ..., nClusters - 1. +

        +
      • +
      + + + + + + + +
        +
      • +

        orderClustersByWeight

        +
        public void orderClustersByWeight(double[] nodeWeights)
        +
        Orders the clusters in decreasing order of their total node weight. + +

        + The total node weight of a cluster equals the sum of the weights of the + nodes belonging to the cluster. +

        +
        +
        Parameters:
        +
        nodeWeights - Node weights
        +
        See Also:
        +
        orderClustersByNNodes()
        +
        +
      • +
      + + + +
        +
      • +

        mergeClusters

        +
        public void mergeClusters(Clustering clustering)
        +
        Merges the clusters based on a clustering of the clusters.
        +
        +
        Parameters:
        +
        clustering - Clustering of the clusters
        +
        +
      • +
      + + + +
        +
      • +

        calcNormalizedMutualInformation

        +
        public double calcNormalizedMutualInformation(Clustering clustering)
        +
        Calculates the normalized mutual information relative to another + clustering.
        +
        +
        Parameters:
        +
        clustering - Other clustering
        +
        Returns:
        +
        Normalized mutual information
        +
        +
      • +
      + + + +
        +
      • +

        calcVariationOfInformation

        +
        public double calcVariationOfInformation(Clustering clustering)
        +
        Calculates the variation of information relative to another clustering.
        +
        +
        Parameters:
        +
        clustering - Other clustering
        +
        Returns:
        +
        Variation of information
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/ClusteringAlgorithm.html b/docs/cwts/networkanalysis/ClusteringAlgorithm.html new file mode 100644 index 0000000..dc0fc33 --- /dev/null +++ b/docs/cwts/networkanalysis/ClusteringAlgorithm.html @@ -0,0 +1,245 @@ + + + + + +ClusteringAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Interface ClusteringAlgorithm

+
+
+
+ +
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        findClustering

        +
        Clustering findClustering(Network network)
        +
        Finds a clustering of the nodes in a network.
        +
        +
        Parameters:
        +
        network - Network
        +
        Returns:
        +
        Clustering
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/ComponentsAlgorithm.html b/docs/cwts/networkanalysis/ComponentsAlgorithm.html new file mode 100644 index 0000000..d5d75c2 --- /dev/null +++ b/docs/cwts/networkanalysis/ComponentsAlgorithm.html @@ -0,0 +1,297 @@ + + + + + +ComponentsAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Class ComponentsAlgorithm

+
+
+
    +
  • java.lang.Object
  • +
  • +
      +
    • cwts.networkanalysis.ComponentsAlgorithm
    • +
    +
  • +
+
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    ClusteringAlgorithm
    +
    +
    +
    +
    public class ComponentsAlgorithm
    +extends java.lang.Object
    +implements ClusteringAlgorithm
    +
    Algorithm for finding the connected components of a network.
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Constructor Summary

      + + + + + + + + +
      Constructors 
      Constructor and Description
      ComponentsAlgorithm() +
      Constructs a components algorithm.
      +
      +
    • +
    + +
      +
    • + + +

      Method Summary

      + + + + + + + + + + +
      All Methods Instance Methods Concrete Methods 
      Modifier and TypeMethod and Description
      ClusteringfindClustering(Network network) +
      Finds the connected components of a network.
      +
      +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        ComponentsAlgorithm

        +
        public ComponentsAlgorithm()
        +
        Constructs a components algorithm.
        +
      • +
      +
    • +
    + + +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/FastLocalMovingAlgorithm.html b/docs/cwts/networkanalysis/FastLocalMovingAlgorithm.html new file mode 100644 index 0000000..1057b08 --- /dev/null +++ b/docs/cwts/networkanalysis/FastLocalMovingAlgorithm.html @@ -0,0 +1,462 @@ + + + + + +FastLocalMovingAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Class FastLocalMovingAlgorithm

+
+
+ +
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    ClusteringAlgorithm, IncrementalClusteringAlgorithm, QualityClusteringAlgorithm
    +
    +
    +
    +
    public class FastLocalMovingAlgorithm
    +extends IterativeCPMClusteringAlgorithm
    +
    Fast local moving algorithm. + +

    + The fast local moving algorithm first adds all nodes in a network to a + queue. It then removes a node from the queue. The node is moved to the + cluster that results in the largest increase in the quality function. If the + current cluster assignment of the node is already optimal, the node is not + moved. If the node is moved to a different cluster, the neighbors of the + node that do not belong to the node's new cluster and that are not yet in + the queue are added to the queue. The algorithm continues removing nodes + from the queue until the queue is empty. +

    + +

    + The fast local moving algorithm provides a fast variant of the StandardLocalMovingAlgorithm. +

    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        random

        +
        protected java.util.Random random
        +
        Random number generator.
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        FastLocalMovingAlgorithm

        +
        public FastLocalMovingAlgorithm()
        +
        Constructs a fast local moving algorithm.
        +
      • +
      + + + +
        +
      • +

        FastLocalMovingAlgorithm

        +
        public FastLocalMovingAlgorithm(double resolution,
        +                                int nIterations,
        +                                java.util.Random random)
        +
        Constructs a fast local moving algorithm for a specified resolution + parameter and number of iterations.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        nIterations - Number of iterations
        +
        random - Random number generator
        +
        +
      • +
      + + + +
        +
      • +

        FastLocalMovingAlgorithm

        +
        public FastLocalMovingAlgorithm(java.util.Random random)
        +
        Constructs a fast local moving algorithm.
        +
        +
        Parameters:
        +
        random - Random number generator
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        improveClusteringOneIteration

        +
        protected boolean improveClusteringOneIteration(Network network,
        +                                                Clustering clustering)
        +
        Improves a clustering by performing one iteration of the fast local + moving algorithm. + +

        + The fast local moving algorithm first adds all nodes in a network to a + queue. It then removes a node from the queue. The node is moved to the + cluster that results in the largest increase in the quality function. If + the current cluster assignment of the node is already optimal, the node + is not moved. If the node is moved to a different cluster, the neighbors + of the node that do not belong to the node's new cluster and that are + not yet in the queue are added to the queue. The algorithm continues + removing nodes from the queue until the queue is empty. +

        +
        +
        Specified by:
        +
        improveClusteringOneIteration in class IterativeCPMClusteringAlgorithm
        +
        Parameters:
        +
        network - Network
        +
        clustering - Clustering
        +
        Returns:
        +
        Boolean indicating whether the clustering has been improved
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/IncrementalCPMClusteringAlgorithm.html b/docs/cwts/networkanalysis/IncrementalCPMClusteringAlgorithm.html new file mode 100644 index 0000000..5ae5ae6 --- /dev/null +++ b/docs/cwts/networkanalysis/IncrementalCPMClusteringAlgorithm.html @@ -0,0 +1,363 @@ + + + + + +IncrementalCPMClusteringAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Class IncrementalCPMClusteringAlgorithm

+
+
+ +
+ +
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        IncrementalCPMClusteringAlgorithm

        +
        public IncrementalCPMClusteringAlgorithm()
        +
        Constructs an incremental CPM clustering algorithm.
        +
      • +
      + + + +
        +
      • +

        IncrementalCPMClusteringAlgorithm

        +
        public IncrementalCPMClusteringAlgorithm(double resolution)
        +
        Constructs an incremental CPM clustering algorithm with a specified + resolution parameter.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        +
      • +
      +
    • +
    + + +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/IncrementalClusteringAlgorithm.html b/docs/cwts/networkanalysis/IncrementalClusteringAlgorithm.html new file mode 100644 index 0000000..2218b62 --- /dev/null +++ b/docs/cwts/networkanalysis/IncrementalClusteringAlgorithm.html @@ -0,0 +1,257 @@ + + + + + +IncrementalClusteringAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Interface IncrementalClusteringAlgorithm

+
+
+
+ +
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        improveClustering

        +
        boolean improveClustering(Network network,
        +                          Clustering clustering)
        +
        Improves a clustering of the nodes in a network.
        +
        +
        Parameters:
        +
        network - Network
        +
        clustering - Clustering
        +
        Returns:
        +
        Boolean indicating whether the clustering has been improved
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/IterativeCPMClusteringAlgorithm.html b/docs/cwts/networkanalysis/IterativeCPMClusteringAlgorithm.html new file mode 100644 index 0000000..7963a7a --- /dev/null +++ b/docs/cwts/networkanalysis/IterativeCPMClusteringAlgorithm.html @@ -0,0 +1,491 @@ + + + + + +IterativeCPMClusteringAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Class IterativeCPMClusteringAlgorithm

+
+
+ +
+ +
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        DEFAULT_N_ITERATIONS

        +
        public static final int DEFAULT_N_ITERATIONS
        +
        Default number of iterations.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        nIterations

        +
        protected int nIterations
        +
        Number of iterations.
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        IterativeCPMClusteringAlgorithm

        +
        public IterativeCPMClusteringAlgorithm()
        +
        Constructs an iterative CPM clustering algorithm.
        +
      • +
      + + + +
        +
      • +

        IterativeCPMClusteringAlgorithm

        +
        public IterativeCPMClusteringAlgorithm(double resolution,
        +                                       int nIterations)
        +
        Constructs an iterative CPM clustering algorithm with a specified + resolution parameter and number of iterations.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        nIterations - Number of iterations
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        getNIterations

        +
        public int getNIterations()
        +
        Returns the number of iterations.
        +
        +
        Returns:
        +
        Number of iterations
        +
        +
      • +
      + + + +
        +
      • +

        setNIterations

        +
        public void setNIterations(int nIterations)
        +
        Sets the number of iterations.
        +
        +
        Parameters:
        +
        nIterations - Number of iterations
        +
        +
      • +
      + + + + + + + +
        +
      • +

        improveClusteringOneIteration

        +
        protected abstract boolean improveClusteringOneIteration(Network network,
        +                                                         Clustering clustering)
        +
        Improves a clustering by performing one iteration of an iterative + clustering algorithm.
        +
        +
        Parameters:
        +
        network - Network
        +
        clustering - Clustering
        +
        Returns:
        +
        Boolean indicating whether the clustering has been improved
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/LeidenAlgorithm.html b/docs/cwts/networkanalysis/LeidenAlgorithm.html new file mode 100644 index 0000000..7b368ac --- /dev/null +++ b/docs/cwts/networkanalysis/LeidenAlgorithm.html @@ -0,0 +1,659 @@ + + + + + +LeidenAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Class LeidenAlgorithm

+
+
+ +
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    ClusteringAlgorithm, IncrementalClusteringAlgorithm, QualityClusteringAlgorithm
    +
    +
    +
    +
    public class LeidenAlgorithm
    +extends IterativeCPMClusteringAlgorithm
    +
    Leiden algorithm. + +

    + The Leiden algorithm consists of three phases: +

    + +
      +
    1. local moving of nodes between clusters,
    2. +
    3. refinement of the clusters,
    4. +
    5. aggregation of the network based on the refined clusters, using the + non-refined clusters to create an initial clustering for the aggregate + network.
    6. +
    + +

    + These phases are repeated until no further improvements can be made. By + default, local moving of nodes is performed using the FastLocalMovingAlgorithm. +

    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        DEFAULT_RANDOMNESS

        +
        public static final double DEFAULT_RANDOMNESS
        +
        Default randomness parameter.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + + + + + +
        +
      • +

        random

        +
        protected java.util.Random random
        +
        Random number generator.
        +
      • +
      + + + +
        +
      • +

        randomness

        +
        protected double randomness
        +
        Randomness parameter.
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        LeidenAlgorithm

        +
        public LeidenAlgorithm()
        +
        Constructs a Leiden algorithm.
        +
      • +
      + + + +
        +
      • +

        LeidenAlgorithm

        +
        public LeidenAlgorithm(double resolution,
        +                       int nIterations,
        +                       double randomness,
        +                       IncrementalCPMClusteringAlgorithm localMovingAlgorithm,
        +                       java.util.Random random)
        +
        Constructs a Leiden algorithm for a specified resolution parameter, + number of iterations, randomness parameter, and local moving algorithm.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        nIterations - Number of iterations
        +
        randomness - Randomness parameter
        +
        localMovingAlgorithm - Local moving algorithm
        +
        random - Random number generator
        +
        +
      • +
      + + + +
        +
      • +

        LeidenAlgorithm

        +
        public LeidenAlgorithm(double resolution,
        +                       int nIterations,
        +                       double randomness,
        +                       java.util.Random random)
        +
        Constructs a Leiden algorithm for a specified resolution parameter, + number of iterations, and randomness parameter.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        nIterations - Number of iterations
        +
        randomness - Randomness parameter
        +
        random - Random number generator
        +
        +
      • +
      + + + +
        +
      • +

        LeidenAlgorithm

        +
        public LeidenAlgorithm(java.util.Random random)
        +
        Constructs a Leiden algorithm.
        +
        +
        Parameters:
        +
        random - Random number generator
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + + + + + +
        +
      • +

        getRandomness

        +
        public double getRandomness()
        +
        Returns the randomness parameter.
        +
        +
        Returns:
        +
        Randomness parameter
        +
        +
      • +
      + + + +
        +
      • +

        getLocalMovingAlgorithm

        +
        public IncrementalCPMClusteringAlgorithm getLocalMovingAlgorithm()
        +
        Returns the local moving algorithm.
        +
        +
        Returns:
        +
        Local moving algorithm
        +
        +
      • +
      + + + +
        +
      • +

        setRandomness

        +
        public void setRandomness(double randomness)
        +
        Sets the randomness parameter.
        +
        +
        Parameters:
        +
        randomness - Randomness parameter
        +
        +
      • +
      + + + +
        +
      • +

        setLocalMovingAlgorithm

        +
        public void setLocalMovingAlgorithm(IncrementalCPMClusteringAlgorithm localMovingAlgorithm)
        +
        Sets the local moving algorithm.
        +
        +
        Parameters:
        +
        localMovingAlgorithm - Local moving algorithm
        +
        +
      • +
      + + + +
        +
      • +

        improveClusteringOneIteration

        +
        protected boolean improveClusteringOneIteration(Network network,
        +                                                Clustering clustering)
        +
        Improves a clustering by performing one iteration of the Leiden + algorithm. + +

        + The Leiden algorithm consists of three phases: +

        + +
          +
        1. local moving of nodes between clusters,
        2. +
        3. refinement of the clusters,
        4. +
        5. aggregation of the network based on the refined clusters, using the + non-refined clusters to create an initial clustering for the aggregate + network.
        6. +
        + +

        + These phases are repeated until no further improvements can be made. +

        +
        +
        Specified by:
        +
        improveClusteringOneIteration in class IterativeCPMClusteringAlgorithm
        +
        Parameters:
        +
        network - Network
        +
        clustering - Clustering
        +
        Returns:
        +
        Boolean indicating whether the clustering has been improved
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/LocalMergingAlgorithm.html b/docs/cwts/networkanalysis/LocalMergingAlgorithm.html new file mode 100644 index 0000000..0a3ff69 --- /dev/null +++ b/docs/cwts/networkanalysis/LocalMergingAlgorithm.html @@ -0,0 +1,512 @@ + + + + + +LocalMergingAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Class LocalMergingAlgorithm

+
+
+ +
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    ClusteringAlgorithm, QualityClusteringAlgorithm
    +
    +
    +
    +
    public class LocalMergingAlgorithm
    +extends CPMClusteringAlgorithm
    +
    Local merging algorithm. + +

    + The local merging algorithm starts from a singleton partition. It performs a + single iteration over the nodes in a network. Each node belonging to a + singleton cluster is considered for merging with another cluster. This + cluster is chosen randomly from all clusters that do not result in a + decrease in the quality function. The larger the increase in the quality + function, the more likely a cluster is to be chosen. The strength of this + effect is determined by the randomness parameter. The higher the value of + the randomness parameter, the stronger the randomness in the choice of a + cluster. The lower the value of the randomness parameter, the more likely + the cluster resulting in the largest increase in the quality function is to + be chosen. A node is merged with a cluster only if both are sufficiently + well connected to the rest of the network. +

    + +

    + The local merging algorithm is used in the cluster refinement phase of the + LeidenAlgorithm. +

    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        DEFAULT_RANDOMNESS

        +
        public static final double DEFAULT_RANDOMNESS
        +
        Default randomness parameter.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        random

        +
        protected java.util.Random random
        +
        Random number generator.
        +
      • +
      + + + +
        +
      • +

        randomness

        +
        protected double randomness
        +
        Randomness parameter.
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        LocalMergingAlgorithm

        +
        public LocalMergingAlgorithm()
        +
        Constructs a local merging algorithm.
        +
      • +
      + + + +
        +
      • +

        LocalMergingAlgorithm

        +
        public LocalMergingAlgorithm(double resolution,
        +                             double randomness,
        +                             java.util.Random random)
        +
        Constructs a local merging algorithm for a specified resolution + parameter and randomness parameter.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        randomness - Randomness parameter
        +
        random - Random number generator
        +
        +
      • +
      + + + +
        +
      • +

        LocalMergingAlgorithm

        +
        public LocalMergingAlgorithm(java.util.Random random)
        +
        Constructs a local merging algorithm.
        +
        +
        Parameters:
        +
        random - Random number generator
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        getRandomness

        +
        public double getRandomness()
        +
        Returns the randomness parameter.
        +
        +
        Returns:
        +
        Randomness
        +
        +
      • +
      + + + +
        +
      • +

        setRandomness

        +
        public void setRandomness(double randomness)
        +
        Sets the randomness parameter.
        +
        +
        Parameters:
        +
        randomness - Randomness
        +
        +
      • +
      + + + +
        +
      • +

        findClustering

        +
        public Clustering findClustering(Network network)
        +
        Finds a clustering of the nodes in a network using the local merging + algorithm. + +

        + The local merging algorithm starts from a singleton partition. It + performs a single iteration over the nodes in a network. Each node + belonging to a singleton cluster is considered for merging with another + cluster. This cluster is chosen randomly from all clusters that do not + result in a decrease in the quality function. The larger the increase in + the quality function, the more likely a cluster is to be chosen. The + strength of this effect is determined by the randomness parameter. The + higher the value of the randomness parameter, the stronger the + randomness in the choice of a cluster. The lower the value of the + randomness parameter, the more likely the cluster resulting in the + largest increase in the quality function is to be chosen. A node is + merged with a cluster only if both are sufficiently well connected to + the rest of the network. +

        +
        +
        Parameters:
        +
        network - Network
        +
        Returns:
        +
        Clustering
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/LouvainAlgorithm.html b/docs/cwts/networkanalysis/LouvainAlgorithm.html new file mode 100644 index 0000000..ef519be --- /dev/null +++ b/docs/cwts/networkanalysis/LouvainAlgorithm.html @@ -0,0 +1,552 @@ + + + + + +LouvainAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Class LouvainAlgorithm

+
+
+ +
+ +
+
+ +
+
+
    +
  • + + + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        LouvainAlgorithm

        +
        public LouvainAlgorithm()
        +
        Constructs a Louvain algorithm.
        +
      • +
      + + + +
        +
      • +

        LouvainAlgorithm

        +
        public LouvainAlgorithm(double resolution,
        +                        int nIterations,
        +                        IncrementalCPMClusteringAlgorithm localMovingAlgorithm)
        +
        Constructs a Louvain algorithm for a specified resolution parameter, + number of iterations, and local moving algorithm.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        nIterations - Number of iterations
        +
        localMovingAlgorithm - Local moving algorithm
        +
        +
      • +
      + + + +
        +
      • +

        LouvainAlgorithm

        +
        public LouvainAlgorithm(double resolution,
        +                        int nIterations,
        +                        java.util.Random random)
        +
        Constructs a Louvain algorithm for a specified resolution parameter and + number of iterations.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        nIterations - Number of iterations
        +
        random - Random number generator
        +
        +
      • +
      + + + +
        +
      • +

        LouvainAlgorithm

        +
        public LouvainAlgorithm(java.util.Random random)
        +
        Constructs a Louvain algorithm.
        +
        +
        Parameters:
        +
        random - Random number generator
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + + + + + +
        +
      • +

        getLocalMovingAlgorithm

        +
        public IncrementalCPMClusteringAlgorithm getLocalMovingAlgorithm()
        +
        Returns the local moving algorithm.
        +
        +
        Returns:
        +
        Local moving algorithm
        +
        +
      • +
      + + + +
        +
      • +

        setLocalMovingAlgorithm

        +
        public void setLocalMovingAlgorithm(IncrementalCPMClusteringAlgorithm localMovingAlgorithm)
        +
        Sets the local moving algorithm.
        +
        +
        Parameters:
        +
        localMovingAlgorithm - Local moving algorithm
        +
        +
      • +
      + + + +
        +
      • +

        improveClusteringOneIteration

        +
        protected boolean improveClusteringOneIteration(Network network,
        +                                                Clustering clustering)
        +
        Improves a clustering by performing one iteration of the Louvain + algorithm. + +

        + The Louvain algorithm consists of two phases: +

        + +
          +
        1. local moving of nodes between clusters,
        2. +
        3. aggregation of the network based on the clusters.
        4. +
        + +

        + These phases are repeated until no further improvements can be made. +

        +
        +
        Specified by:
        +
        improveClusteringOneIteration in class IterativeCPMClusteringAlgorithm
        +
        Parameters:
        +
        network - Network
        +
        clustering - Clustering
        +
        Returns:
        +
        Boolean indicating whether the clustering has been improved
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/Network.html b/docs/cwts/networkanalysis/Network.html new file mode 100644 index 0000000..5138e6b --- /dev/null +++ b/docs/cwts/networkanalysis/Network.html @@ -0,0 +1,1672 @@ + + + + + +Network + + + + + + + + +
+ + + + +
+ + +
+ +

Class Network

+
+
+
    +
  • java.lang.Object
  • +
  • +
      +
    • cwts.networkanalysis.Network
    • +
    +
  • +
+
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    java.io.Serializable
    +
    +
    +
    +
    public class Network
    +extends java.lang.Object
    +implements java.io.Serializable
    +
    Network. + +

    + Weighted nodes and weighted edges are supported. Directed edges are not + supported. +

    + +

    + Network objects are immutable. +

    + +

    + The adjacency matrix of the network is stored in a sparse compressed format. +

    +
    +
    See Also:
    +
    Serialized Form
    +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Summary

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Fields 
      Modifier and TypeField and Description
      protected double[]edgeWeights +
      Edge weights.
      +
      protected int[]firstNeighborIndices +
      Index of the first neighbor of each node in the (@code neighbors} array.
      +
      protected intnEdges +
      Number of edges.
      +
      protected int[]neighbors +
      Neighbors of each node.
      +
      protected intnNodes +
      Number of nodes.
      +
      protected double[]nodeWeights +
      Node weights.
      +
      protected doubletotalEdgeWeightSelfLinks +
      Total edge weight of self links.
      +
      +
    • +
    + +
      +
    • + + +

      Constructor Summary

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Constructors 
      Constructor and Description
      Network(double[] nodeWeights, + int[][] edges, + boolean sortedEdges, + boolean checkIntegrity) +
      Constructs a network based on a list of edges.
      +
      Network(double[] nodeWeights, + int[][] edges, + double[] edgeWeights, + boolean sortedEdges, + boolean checkIntegrity) +
      Constructs a network based on a list of edges.
      +
      Network(double[] nodeWeights, + int[] firstNeighborIndices, + int[] neighbors, + boolean checkIntegrity) +
      Constructs a network based on a list of neighbors.
      +
      Network(double[] nodeWeights, + int[] firstNeighborIndices, + int[] neighbors, + double[] edgeWeights, + boolean checkIntegrity) +
      Constructs a network based on a list of neighbors.
      +
      Network(int nNodes, + boolean setNodeWeightsToTotalEdgeWeights, + int[][] edges, + boolean sortedEdges, + boolean checkIntegrity) +
      Constructs a network based on a list of edges.
      +
      Network(int nNodes, + boolean setNodeWeightsToTotalEdgeWeights, + int[][] edges, + double[] edgeWeights, + boolean sortedEdges, + boolean checkIntegrity) +
      Constructs a network based on a list of edges.
      +
      Network(int nNodes, + boolean setNodeWeightsToTotalEdgeWeights, + int[] firstNeighborIndices, + int[] neighbors, + boolean checkIntegrity) +
      Constructs a network based on a list of neighbors.
      +
      Network(int nNodes, + boolean setNodeWeightsToTotalEdgeWeights, + int[] firstNeighborIndices, + int[] neighbors, + double[] edgeWeights, + boolean checkIntegrity) +
      Constructs a network based on a list of neighbors.
      +
      +
    • +
    + + +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        edgeWeights

        +
        protected double[] edgeWeights
        +
        Edge weights.
        +
      • +
      + + + +
        +
      • +

        firstNeighborIndices

        +
        protected int[] firstNeighborIndices
        +
        Index of the first neighbor of each node in the (@code neighbors} array. + +

        + The neighbors of node i are given by + neighbors[firstNeighborIndices[i]], ..., + neighbors[firstNeighborIndices[i + 1] - 1]. +

        +
      • +
      + + + +
        +
      • +

        nEdges

        +
        protected int nEdges
        +
        Number of edges. + +

        + Each edge is counted twice, once in each direction. +

        +
      • +
      + + + +
        +
      • +

        neighbors

        +
        protected int[] neighbors
        +
        Neighbors of each node.
        +
      • +
      + + + +
        +
      • +

        nNodes

        +
        protected int nNodes
        +
        Number of nodes.
        +
      • +
      + + + +
        +
      • +

        nodeWeights

        +
        protected double[] nodeWeights
        +
        Node weights.
        +
      • +
      + + + +
        +
      • +

        totalEdgeWeightSelfLinks

        +
        protected double totalEdgeWeightSelfLinks
        +
        Total edge weight of self links.
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        Network

        +
        public Network(double[] nodeWeights,
        +               int[][] edges,
        +               boolean sortedEdges,
        +               boolean checkIntegrity)
        +
        Constructs a network based on a list of edges. + +

        + The list of edges is provided in the two-dimensional array + edges. Edge i connects nodes edges[0][i] and + edges[1][i]. Edges do not have weights. If sortedEdges is + false, the list of edges does not need to be sorted and each edge must + be included only once. If sortedEdgesis true, the list of edges + must be sorted and each edge must be included twice, once in each + direction. +

        +
        +
        Parameters:
        +
        nodeWeights - Node weights
        +
        edges - Edge list
        +
        sortedEdges - Indicates whether the edge list is sorted
        +
        checkIntegrity - Indicates whether to check the integrity of the + network
        +
        +
      • +
      + + + +
        +
      • +

        Network

        +
        public Network(double[] nodeWeights,
        +               int[][] edges,
        +               double[] edgeWeights,
        +               boolean sortedEdges,
        +               boolean checkIntegrity)
        +
        Constructs a network based on a list of edges. + +

        + The list of edges is provided in the two-dimensional array + edges. Edge i connects nodes edges[0][i] and + edges[1][i] and has weight edgeWeights[i]. If + sortedEdges is false, the list of edges does not need to be sorted and + each edge must be included only once. If sortedEdges is true, + the list of edges must be sorted and each edge must be included twice, + once in each direction. +

        +
        +
        Parameters:
        +
        nodeWeights - Node weights
        +
        edges - Edge list
        +
        edgeWeights - Edge weights
        +
        sortedEdges - Indicates whether the edge list is sorted
        +
        checkIntegrity - Indicates whether to check the integrity of the + network
        +
        +
      • +
      + + + +
        +
      • +

        Network

        +
        public Network(double[] nodeWeights,
        +               int[] firstNeighborIndices,
        +               int[] neighbors,
        +               boolean checkIntegrity)
        +
        Constructs a network based on a list of neighbors. + +

        + The list of neighbors is provided in the array neighbors. The + neighbors of node i are given by + neighbors[firstNeighborIndices[i]], ..., + neighbors[firstNeighborIndices[i + 1] - 1]. The array + firstNeighborIndices must have a length of the number of nodes plus 1. + The neighbors of a node must be listed in increasing order in the array + neighbors. Edges do not have weights. +

        +
        +
        Parameters:
        +
        nodeWeights - Node weights
        +
        firstNeighborIndices - Index of the first neighbor of each node
        +
        neighbors - Neighbor list
        +
        checkIntegrity - Indicates whether to check the integrity of + the network
        +
        +
      • +
      + + + +
        +
      • +

        Network

        +
        public Network(double[] nodeWeights,
        +               int[] firstNeighborIndices,
        +               int[] neighbors,
        +               double[] edgeWeights,
        +               boolean checkIntegrity)
        +
        Constructs a network based on a list of neighbors. + +

        + The list of neighbors is provided in the array neighbors. The + neighbors of node i are given by + neighbors[firstNeighborIndices[i]], ..., + neighbors[firstNeighborIndices[i + 1] - 1]. The array + firstNeighborIndices must have a length of the number of nodes plus 1. + The neighbors of a node must be listed in increasing order in the array + neighbors. For each neighbor in the array neighbors, the + corresponding edge weight is provided in the array edgeWeights. +

        +
        +
        Parameters:
        +
        nodeWeights - Node weights
        +
        firstNeighborIndices - Index of the first neighbor of each node
        +
        neighbors - Neighbor list
        +
        edgeWeights - Edge weights
        +
        checkIntegrity - Indicates whether to check the integrity of + the network
        +
        +
      • +
      + + + +
        +
      • +

        Network

        +
        public Network(int nNodes,
        +               boolean setNodeWeightsToTotalEdgeWeights,
        +               int[][] edges,
        +               boolean sortedEdges,
        +               boolean checkIntegrity)
        +
        Constructs a network based on a list of edges. + +

        + The list of edges is provided in the two-dimensional array + edges. Edge i connects nodes edges[0][i] and + edges[1][i]. Edges do not have weights. If sortedEdges is + false, the list of edges does not need to be sorted and each edge must + be included only once. If sortedEdgesis true, the list of edges + must be sorted and each edge must be included twice, once in each + direction. +

        + +

        + If setNodeWeightsToTotalEdgeWeights is false, the weights of the + nodes are set to 1. If setNodeWeightsToTotalEdgeWeights is true, + the weight of a node is set equal to the total weight of the edges + between the node and its neighbors. +

        +
        +
        Parameters:
        +
        nNodes - Number of nodes
        +
        setNodeWeightsToTotalEdgeWeights - Indicates whether to set node + weights equal to total edge + weights
        +
        edges - Edge list
        +
        sortedEdges - Indicates whether the edge list + is sorted
        +
        checkIntegrity - Indicates whether to check the + integrity of the network
        +
        +
      • +
      + + + +
        +
      • +

        Network

        +
        public Network(int nNodes,
        +               boolean setNodeWeightsToTotalEdgeWeights,
        +               int[][] edges,
        +               double[] edgeWeights,
        +               boolean sortedEdges,
        +               boolean checkIntegrity)
        +
        Constructs a network based on a list of edges. + +

        + The list of edges is provided in the two-dimensional array + edges. Edge i connects nodes edges[0][i] and + edges[1][i] and has weight edgeWeights[i]. If + sortedEdges is false, the list of edges does not need to be sorted and + each edge must be included only once. If sortedEdges is true, + the list of edges must be sorted and each edge must be included twice, + once in each direction. +

        + +

        + If setNodeWeightsToTotalEdgeWeights is false, the weights of the + nodes are set to 1. If setNodeWeightsToTotalEdgeWeights is true, + the weight of a node is set equal to the total weight of the edges + between the node and its neighbors. +

        +
        +
        Parameters:
        +
        nNodes - Number of nodes
        +
        setNodeWeightsToTotalEdgeWeights - Indicates whether to set node + weights equal to total edge + weights
        +
        edges - Edge list
        +
        edgeWeights - Edge weights
        +
        sortedEdges - Indicates whether the edge list + is sorted
        +
        checkIntegrity - Indicates whether to check the + integrity of the network
        +
        +
      • +
      + + + +
        +
      • +

        Network

        +
        public Network(int nNodes,
        +               boolean setNodeWeightsToTotalEdgeWeights,
        +               int[] firstNeighborIndices,
        +               int[] neighbors,
        +               boolean checkIntegrity)
        +
        Constructs a network based on a list of neighbors. + +

        + The list of neighbors is provided in the array neighbors. The + neighbors of node i are given by + neighbors[firstNeighborIndices[i]], ..., + neighbors[firstNeighborIndices[i + 1] - 1]. The array + firstNeighborIndices must have a length of the number of nodes plus 1. + The neighbors of a node must be listed in increasing order in the array + neighbors. Edges do not have weights. +

        + +

        + If setNodeWeightsToTotalEdgeWeights is false, the weights of the + nodes are set to 1. If setNodeWeightsToTotalEdgeWeights is true, + the weight of a node is set equal to the total weight of the edges + between the node and its neighbors. +

        +
        +
        Parameters:
        +
        nNodes - Number of nodes
        +
        setNodeWeightsToTotalEdgeWeights - Indicates whether to set node + weights equal to total edge + weights
        +
        firstNeighborIndices - Index of the first neighbor of + each node
        +
        neighbors - Neighbor list
        +
        checkIntegrity - Indicates whether to check the + integrity of the network
        +
        +
      • +
      + + + +
        +
      • +

        Network

        +
        public Network(int nNodes,
        +               boolean setNodeWeightsToTotalEdgeWeights,
        +               int[] firstNeighborIndices,
        +               int[] neighbors,
        +               double[] edgeWeights,
        +               boolean checkIntegrity)
        +
        Constructs a network based on a list of neighbors. + +

        + The list of neighbors is provided in the array neighbors. The + neighbors of node i are given by + neighbors[firstNeighborIndices[i]], ..., + neighbors[firstNeighborIndices[i + 1] - 1]. The array + firstNeighborIndices must have a length of the number of nodes plus 1. + The neighbors of a node must be listed in increasing order in the array + neighbors. For each neighbor in the array neighbors, the + corresponding edge weight is provided in the array edgeWeights. +

        + +

        + If setNodeWeightsToTotalEdgeWeights is false, the weights of the + nodes are set to 1. If setNodeWeightsToTotalEdgeWeights is true, + the weight of a node is set equal to the total weight of the edges + between the node and its neighbors. +

        +
        +
        Parameters:
        +
        nNodes - Number of nodes
        +
        setNodeWeightsToTotalEdgeWeights - Indicates whether to set node + weights equal to total edge + weights
        +
        firstNeighborIndices - Index of the first neighbor of + each node
        +
        neighbors - Neighbor list
        +
        edgeWeights - Edge weights
        +
        checkIntegrity - Indicates whether to check the + integrity of the network
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        load

        +
        public static Network load(java.lang.String filename)
        +                    throws java.lang.ClassNotFoundException,
        +                           java.io.IOException
        +
        Loads a network from a file.
        +
        +
        Parameters:
        +
        filename - File from which a network is loaded
        +
        Returns:
        +
        Loaded network
        +
        Throws:
        +
        java.lang.ClassNotFoundException - Class not found
        +
        java.io.IOException - Could not read the file
        +
        See Also:
        +
        save(String filename)
        +
        +
      • +
      + + + +
        +
      • +

        save

        +
        public void save(java.lang.String filename)
        +          throws java.io.IOException
        +
        Saves the network in a file.
        +
        +
        Parameters:
        +
        filename - File in which the network is saved
        +
        Throws:
        +
        java.io.IOException - Could not write to the file
        +
        See Also:
        +
        load(String filename)
        +
        +
      • +
      + + + +
        +
      • +

        getNNodes

        +
        public int getNNodes()
        +
        Returns the number of nodes.
        +
        +
        Returns:
        +
        Number of nodes
        +
        +
      • +
      + + + +
        +
      • +

        getTotalNodeWeight

        +
        public double getTotalNodeWeight()
        +
        Returns the total node weight.
        +
        +
        Returns:
        +
        Total node weight
        +
        +
      • +
      + + + +
        +
      • +

        getNodeWeights

        +
        public double[] getNodeWeights()
        +
        Returns the weight of each node.
        +
        +
        Returns:
        +
        Weight of each node
        +
        +
      • +
      + + + +
        +
      • +

        getNodeWeight

        +
        public double getNodeWeight(int node)
        +
        Returns the weight of a node.
        +
        +
        Parameters:
        +
        node - Node
        +
        Returns:
        +
        Weight
        +
        +
      • +
      + + + +
        +
      • +

        getNEdges

        +
        public int getNEdges()
        +
        Returns the number of edges. + +

        + Each edge is counted only once, even though an edge runs in two + directions. This means that the number of edges returned by getEdges() equals twice the number of edges returned by getNEdges(). +

        +
        +
        Returns:
        +
        Number of edges
        +
        +
      • +
      + + + +
        +
      • +

        getNNeighborsPerNode

        +
        public int[] getNNeighborsPerNode()
        +
        Returns the number of neighbors per node.
        +
        +
        Returns:
        +
        Number of neighbors per node
        +
        +
      • +
      + + + +
        +
      • +

        getNNeighbors

        +
        public int getNNeighbors(int node)
        +
        Returns the number of neighbors of a node.
        +
        +
        Parameters:
        +
        node - Node
        +
        Returns:
        +
        Number of neighbors
        +
        +
      • +
      + + + +
        +
      • +

        getEdges

        +
        public int[][] getEdges()
        +
        Returns the list of edges. + +

        + Each edge is included twice, once in each direction. This means that the + number of edges returned by getEdges() equals twice the number + of edges returned by getNEdges(). +

        + +

        + The list of edges is returned in a two-dimensional array edges. + Edge i connects nodes edges[0][i] and + edges[1][i]. +

        +
        +
        Returns:
        +
        List of edges
        +
        +
      • +
      + + + +
        +
      • +

        getNeighborsPerNode

        +
        public int[][] getNeighborsPerNode()
        +
        Returns a list of neighbors per node.
        +
        +
        Returns:
        +
        List of neighbors per node
        +
        +
      • +
      + + + +
        +
      • +

        getNeighbors

        +
        public int[] getNeighbors(int node)
        +
        Returns the list of neighbors of a node.
        +
        +
        Parameters:
        +
        node - Node
        +
        Returns:
        +
        List of neighbors
        +
        +
      • +
      + + + +
        +
      • +

        getTotalEdgeWeight

        +
        public double getTotalEdgeWeight()
        +
        Returns the total edge weight. + +

        + Each edge is considered only once, even though an edge runs in two + directions. This means that the sum of the edge weights returned by + getEdgeWeights() equals twice the total edge weight returned by + getTotalEdgeWeight(). +

        + +

        + Edge weights of self links are not included. +

        +
        +
        Returns:
        +
        Total edge weight
        +
        +
      • +
      + + + +
        +
      • +

        getTotalEdgeWeightPerNode

        +
        public double[] getTotalEdgeWeightPerNode()
        +
        Returns the total edge weight per node. The total edge weight of a node + equals the sum of the weights of the edges between the node and its + neighbors.
        +
        +
        Returns:
        +
        Total edge weight per node
        +
        +
      • +
      + + + +
        +
      • +

        getTotalEdgeWeight

        +
        public double getTotalEdgeWeight(int node)
        +
        Returns the total edge weight of a node. The total edge weight of a node + equals the sum of the weights of the edges between the node and its + neighbors.
        +
        +
        Parameters:
        +
        node - Node
        +
        Returns:
        +
        Total edge weight
        +
        +
      • +
      + + + +
        +
      • +

        getEdgeWeights

        +
        public double[] getEdgeWeights()
        +
        Returns the edge weights. + +

        + Each edge is included twice, once in each direction. This means that the + sum of the edge weights returned by getEdgeWeights() equals + twice the total edge weight returned by getTotalEdgeWeight(). +

        +
        +
        Returns:
        +
        Edge weights
        +
        +
      • +
      + + + +
        +
      • +

        getEdgeWeightsPerNode

        +
        public double[][] getEdgeWeightsPerNode()
        +
        Returns a list of edge weights per node. These are the weights of the + edges between a node and its neighbors.
        +
        +
        Returns:
        +
        List of edge weights per node
        +
        +
      • +
      + + + +
        +
      • +

        getEdgeWeights

        +
        public double[] getEdgeWeights(int node)
        +
        Returns the list of edge weights of a node. These are the weights of the + edges between the node and its neighbors.
        +
        +
        Parameters:
        +
        node - Node
        +
        Returns:
        +
        List of edge weights
        +
        +
      • +
      + + + +
        +
      • +

        getTotalEdgeWeightSelfLinks

        +
        public double getTotalEdgeWeightSelfLinks()
        +
        Returns the total edge weight of self links.
        +
        +
        Returns:
        +
        Total edge weight of self links
        +
        +
      • +
      + + + +
        +
      • +

        createNetworkWithoutNodeWeights

        +
        public Network createNetworkWithoutNodeWeights()
        +
        Creates a copy of the network, but without node weights. + +

        + Each node is assigned a weight of 1. +

        +
        +
        Returns:
        +
        Network without node weights
        +
        +
      • +
      + + + +
        +
      • +

        createNetworkWithoutEdgeWeights

        +
        public Network createNetworkWithoutEdgeWeights()
        +
        Creates a copy of the network, but without edge weights. + +

        + Each edge is assigned a weight of 1. +

        +
        +
        Returns:
        +
        Network without edge weights
        +
        +
      • +
      + + + +
        +
      • +

        createNetworkWithoutNodeAndEdgeWeights

        +
        public Network createNetworkWithoutNodeAndEdgeWeights()
        +
        Creates a copy of the network, but without node and edge weights. + +

        + Each node is assigned a weight of 1, and each edge is assigned a weight + of 1. +

        +
        +
        Returns:
        +
        Network without node and edge weights
        +
        +
      • +
      + + + +
        +
      • +

        createNormalizedNetworkUsingAssociationStrength

        +
        public Network createNormalizedNetworkUsingAssociationStrength()
        +
        Creates a copy of the network in which the edge weights have been + normalized using the association strength. + +

        + The normalized weight a'[i][j] of the edge between nodes + i and j is given by +

        + +
        + a'[i][j] = a[i][j] / (n[i] * n[j] / (2 * m)), +
        + +

        + where a[i][j] is the non-normalized weight of the edge between + nodes i and j, n[i] is the weight of node + i, and m is half the total node weight. +

        + +

        + If each node's weight equals the total weight of the edges between the + node and its neighbors, the edge weights are normalized by dividing them + by the expected edge weights in the random configuration model. +

        + +

        + The node weights are set to 1. +

        +
        +
        Returns:
        +
        Normalized network
        +
        +
      • +
      + + + +
        +
      • +

        createNormalizedNetworkUsingFractionalization

        +
        public Network createNormalizedNetworkUsingFractionalization()
        +
        Creates a copy of the network in which the edge weights have been + normalized using fractionalization. + +

        + The normalized weight a'[i][j] of the edge between nodes + i and j is given by +

        + +
        + a'[i][j] = a[i][j] * (n / n[i] + n / n[j]) / 2, +
        + +

        + where a[i][j] is the non-normalized weight of the edge between + nodes i and j, n[i] is the weight of node + i, and n is the number of nodes. +

        + +

        + The node weights are set to 1. +

        +
        +
        Returns:
        +
        Normalized network
        +
        +
      • +
      + + + +
        +
      • +

        createPrunedNetwork

        +
        public Network createPrunedNetwork(int maxNEdges)
        +
        Creates a copy of the network that has been pruned in order to have a + specified maximum number of edges. + +

        + Only the edges with the highest weights are retained in the pruned + network. In case of ties, the edges to be retained are selected + randomly. +

        +
        +
        Parameters:
        +
        maxNEdges - Maximum number of edges
        +
        Returns:
        +
        Pruned network
        +
        +
      • +
      + + + +
        +
      • +

        createPrunedNetwork

        +
        public Network createPrunedNetwork(int maxNEdges,
        +                                   java.util.Random random)
        +
        Creates a copy of the network that has been pruned in order to have a + specified maximum number of edges. + +

        + Only the edges with the highest weights are retained in the pruned + network. In case of ties, the edges to be retained are selected + randomly. +

        +
        +
        Parameters:
        +
        maxNEdges - Maximum number of edges
        +
        random - Random number generator
        +
        Returns:
        +
        Pruned network
        +
        +
      • +
      + + + +
        +
      • +

        createSubnetwork

        +
        public Network createSubnetwork(int[] nodes)
        +
        Creates an induced subnetwork for specified nodes.
        +
        +
        Parameters:
        +
        nodes - Nodes
        +
        Returns:
        +
        Subnetwork
        +
        +
      • +
      + + + +
        +
      • +

        createSubnetwork

        +
        public Network createSubnetwork(boolean[] nodesInSubnetwork)
        +
        Creates an induced subnetwork for specified nodes.
        +
        +
        Parameters:
        +
        nodesInSubnetwork - Indicates the nodes to be included in the + subnetwork.
        +
        Returns:
        +
        Subnetwork
        +
        +
      • +
      + + + +
        +
      • +

        createSubnetwork

        +
        public Network createSubnetwork(Clustering clustering,
        +                                int cluster)
        +
        Creates an induced subnetwork for a specified cluster in a clustering. + +

        + If subnetworks need to be created for all clusters in a clustering, it + is more efficient to use createSubnetworks(Clustering + clustering). +

        +
        +
        Parameters:
        +
        clustering - Clustering
        +
        cluster - Cluster
        +
        Returns:
        +
        Subnetwork
        +
        +
      • +
      + + + +
        +
      • +

        createSubnetworks

        +
        public Network[] createSubnetworks(Clustering clustering)
        +
        Creates induced subnetworks for the clusters in a clustering.
        +
        +
        Parameters:
        +
        clustering - Clustering
        +
        Returns:
        +
        Subnetworks
        +
        +
      • +
      + + + +
        +
      • +

        createSubnetworkLargestComponent

        +
        public Network createSubnetworkLargestComponent()
        +
        Creates an induced subnetwork of the largest connected component.
        +
        +
        Returns:
        +
        Subnetwork
        +
        +
      • +
      + + + +
        +
      • +

        createReducedNetwork

        +
        public Network createReducedNetwork(Clustering clustering)
        +
        Creates a reduced (or aggregate) network based on a clustering. + +

        + Each node in the reduced network corresponds to a cluster of nodes in + the original network. The weight of a node in the reduced network equals + the sum of the weights of the nodes in the corresponding cluster in the + original network. The weight of an edge between two nodes in the reduced + network equals the sum of the weights of the edges between the nodes in + the two corresponding clusters in the original network. +

        +
        +
        Parameters:
        +
        clustering - Clustering
        +
        Returns:
        +
        Reduced network
        +
        +
      • +
      + + + +
        +
      • +

        identifyComponents

        +
        public Clustering identifyComponents()
        +
        Identifies the connected components of the network.
        +
        +
        Returns:
        +
        Connected components
        +
        +
      • +
      + + + +
        +
      • +

        checkIntegrity

        +
        public void checkIntegrity()
        +                    throws java.lang.IllegalArgumentException
        +
        Checks the integrity of the network. + +

        + It is checked whether: +

        + +
          +
        • variables have a correct value,
        • +
        • arrays have a correct length,
        • +
        • edges are sorted correctly,
        • +
        • edges are stored in both directions.
        • +
        + +

        + An exception is thrown if the integrity of the network is violated. +

        +
        +
        Throws:
        +
        java.lang.IllegalArgumentException - An illegal argument was provided in the + construction of the network.
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/QualityClusteringAlgorithm.html b/docs/cwts/networkanalysis/QualityClusteringAlgorithm.html new file mode 100644 index 0000000..64fcd24 --- /dev/null +++ b/docs/cwts/networkanalysis/QualityClusteringAlgorithm.html @@ -0,0 +1,256 @@ + + + + + +QualityClusteringAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Interface QualityClusteringAlgorithm

+
+
+
+ +
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        calcQuality

        +
        double calcQuality(Network network,
        +                   Clustering clustering)
        +
        Calculates the quality of a clustering of the nodes in a network.
        +
        +
        Parameters:
        +
        network - Network
        +
        clustering - Clustering
        +
        Returns:
        +
        Quality of the clustering
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/RunNetworkClustering.html b/docs/cwts/networkanalysis/RunNetworkClustering.html new file mode 100644 index 0000000..a3866f1 --- /dev/null +++ b/docs/cwts/networkanalysis/RunNetworkClustering.html @@ -0,0 +1,539 @@ + + + + + +RunNetworkClustering + + + + + + + + +
+ + + + +
+ + +
+ +

Class RunNetworkClustering

+
+
+
    +
  • java.lang.Object
  • +
  • +
      +
    • cwts.networkanalysis.RunNetworkClustering
    • +
    +
  • +
+
+
    +
  • +
    +
    +
    public final class RunNetworkClustering
    +extends java.lang.Object
    +
    Command line tool for running the Leiden and Louvain algorithms for network + clustering. + +

    + All methods in this class are static. +

    +
  • +
+
+
+
    +
  • + + + +
      +
    • + + +

      Method Summary

      + + + + + + + + + + + + + + + + + + + + + + +
      All Methods Static Methods Concrete Methods 
      Modifier and TypeMethod and Description
      static voidmain(java.lang.String[] args) +
      This method is called when the tool is started.
      +
      static ClusteringreadClustering(java.lang.String filename, + int nNodes) +
      Reads a clustering from a file.
      +
      static NetworkreadEdgeList(java.lang.String filename, + boolean useModularity, + boolean weightedEdges, + boolean sortedEdgeList) +
      Reads an edge list from a file and creates a network.
      +
      static voidwriteClustering(java.lang.String filename, + Clustering clustering) +
      Writes a clustering to a file.
      +
      +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        COLUMN_SEPARATOR

        +
        public static final java.lang.String COLUMN_SEPARATOR
        +
        Column separator for edge list and clustering files.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        DEFAULT_N_ITERATIONS

        +
        public static final int DEFAULT_N_ITERATIONS
        +
        Default number of iterations.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        DEFAULT_N_RANDOM_STARTS

        +
        public static final int DEFAULT_N_RANDOM_STARTS
        +
        Default number of random starts.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        DEFAULT_RANDOMNESS

        +
        public static final double DEFAULT_RANDOMNESS
        +
        Default randomness parameter.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        DEFAULT_RESOLUTION

        +
        public static final double DEFAULT_RESOLUTION
        +
        Default resolution parameter.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        DEFAULT_USE_LOUVAIN

        +
        public static final boolean DEFAULT_USE_LOUVAIN
        +
        Default clustering algorithm.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        DEFAULT_USE_MODULARITY

        +
        public static final boolean DEFAULT_USE_MODULARITY
        +
        Default quality function.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        DESCRIPTION

        +
        public static final java.lang.String DESCRIPTION
        +
        Description text.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        USAGE

        +
        public static final java.lang.String USAGE
        +
        Usage text.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        main

        +
        public static void main(java.lang.String[] args)
        +
        This method is called when the tool is started.
        +
        +
        Parameters:
        +
        args - Command line arguments
        +
        +
      • +
      + + + +
        +
      • +

        readEdgeList

        +
        public static Network readEdgeList(java.lang.String filename,
        +                                   boolean useModularity,
        +                                   boolean weightedEdges,
        +                                   boolean sortedEdgeList)
        +
        Reads an edge list from a file and creates a network.
        +
        +
        Parameters:
        +
        filename - Filename
        +
        useModularity - Indicates whether to use the modularity or the CPM + quality function
        +
        weightedEdges - Indicates whether edges have weights
        +
        sortedEdgeList - Indicates whether the edge list is sorted
        +
        Returns:
        +
        Network
        +
        +
      • +
      + + + +
        +
      • +

        readClustering

        +
        public static Clustering readClustering(java.lang.String filename,
        +                                        int nNodes)
        +
        Reads a clustering from a file.
        +
        +
        Parameters:
        +
        filename - Filename
        +
        nNodes - Number of nodes
        +
        Returns:
        +
        Clustering
        +
        +
      • +
      + + + +
        +
      • +

        writeClustering

        +
        public static void writeClustering(java.lang.String filename,
        +                                   Clustering clustering)
        +
        Writes a clustering to a file.
        +
        +
        Parameters:
        +
        filename - Filename
        +
        clustering - Clustering
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/StandardLocalMovingAlgorithm.html b/docs/cwts/networkanalysis/StandardLocalMovingAlgorithm.html new file mode 100644 index 0000000..7def51e --- /dev/null +++ b/docs/cwts/networkanalysis/StandardLocalMovingAlgorithm.html @@ -0,0 +1,434 @@ + + + + + +StandardLocalMovingAlgorithm + + + + + + + + +
+ + + + +
+ + +
+ +

Class StandardLocalMovingAlgorithm

+
+
+ +
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    ClusteringAlgorithm, IncrementalClusteringAlgorithm, QualityClusteringAlgorithm
    +
    +
    +
    +
    public class StandardLocalMovingAlgorithm
    +extends IncrementalCPMClusteringAlgorithm
    +
    Standard local moving algorithm. + +

    + The standard local moving algorithm iterates over the nodes in a network. A + node is moved to the cluster that results in the largest increase in the + quality function. If the current cluster assignment of the node is already + optimal, the node is not moved. The algorithm continues iterating over the + nodes in a network until no more nodes can be moved. +

    + +

    + A fast variant of the standard local moving algorithm is provided by the + FastLocalMovingAlgorithm. +

    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        random

        +
        protected java.util.Random random
        +
        Random number generator.
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        StandardLocalMovingAlgorithm

        +
        public StandardLocalMovingAlgorithm()
        +
        Constructs a standard local moving algorithm.
        +
      • +
      + + + +
        +
      • +

        StandardLocalMovingAlgorithm

        +
        public StandardLocalMovingAlgorithm(double resolution,
        +                                    java.util.Random random)
        +
        Constructs a standard local moving algorithm for a specified resolution + parameter.
        +
        +
        Parameters:
        +
        resolution - Resolution parameter
        +
        random - Random number generator
        +
        +
      • +
      + + + +
        +
      • +

        StandardLocalMovingAlgorithm

        +
        public StandardLocalMovingAlgorithm(java.util.Random random)
        +
        Constructs a standard local moving algorithm.
        +
        +
        Parameters:
        +
        random - Random number generator
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        improveClustering

        +
        public boolean improveClustering(Network network,
        +                                 Clustering clustering)
        +
        Improves a clustering of the nodes in a network using the standard local + moving algorithm. + +

        + The standard local moving algorithm iterates over the nodes in a + network. A node is moved to the cluster that results in the largest + increase in the quality function. If the current cluster assignment of + the node is already optimal, the node is not moved. The algorithm + continues iterating over the nodes in a network until no more nodes can + be moved. +

        +
        +
        Parameters:
        +
        network - Network
        +
        clustering - Clustering
        +
        Returns:
        +
        Boolean indicating whether the clustering has been improved
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/networkanalysis/package-frame.html b/docs/cwts/networkanalysis/package-frame.html new file mode 100644 index 0000000..56a472d --- /dev/null +++ b/docs/cwts/networkanalysis/package-frame.html @@ -0,0 +1,37 @@ + + + + + +cwts.networkanalysis + + + + + + +

cwts.networkanalysis

+ + + diff --git a/docs/cwts/networkanalysis/package-summary.html b/docs/cwts/networkanalysis/package-summary.html new file mode 100644 index 0000000..bb8eb63 --- /dev/null +++ b/docs/cwts/networkanalysis/package-summary.html @@ -0,0 +1,265 @@ + + + + + +cwts.networkanalysis + + + + + + + + +
+ + + + +
+ +
+

Package cwts.networkanalysis

+
+
Provides data structures and algorithms for network analysis.
+
+

See: Description

+
+
+ + + + +

Package cwts.networkanalysis Description

+
Provides data structures and algorithms for network analysis. + +

+ Currently, the focus of this package is restricted to clustering algorithms + (also known as community detection algorithms) and the associated data + structures. +

+ +

+ The classes Network and Clustering represent the core data structures. The + classes LeidenAlgorithm and LouvainAlgorithm represent the core clustering + algorithms. These two classes are embedded in a hierarchy of classes and + interfaces for representing clustering algorithms. +

+
+ + + + + + diff --git a/docs/cwts/networkanalysis/package-tree.html b/docs/cwts/networkanalysis/package-tree.html new file mode 100644 index 0000000..b60c09d --- /dev/null +++ b/docs/cwts/networkanalysis/package-tree.html @@ -0,0 +1,171 @@ + + + + + +cwts.networkanalysis Class Hierarchy + + + + + + + + +
+ + + + +
+ +
+

Hierarchy For Package cwts.networkanalysis

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +

Interface Hierarchy

+ +
+ + + + + + diff --git a/docs/cwts/util/Arrays.html b/docs/cwts/util/Arrays.html new file mode 100644 index 0000000..dab1cc6 --- /dev/null +++ b/docs/cwts/util/Arrays.html @@ -0,0 +1,550 @@ + + + + + +Arrays + + + + + + + + +
+ + + + +
+ + +
+
Package cwts.util
+

Class Arrays

+
+
+
    +
  • java.lang.Object
  • +
  • +
      +
    • cwts.util.Arrays
    • +
    +
  • +
+
+
    +
  • +
    +
    +
    public final class Arrays
    +extends java.lang.Object
    +
    Utility functions for arrays. + +

    + All methods in this class are static. +

    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Method Summary

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      All Methods Static Methods Concrete Methods 
      Modifier and TypeMethod and Description
      static doublecalcAverage(double[] values) +
      Calculates the average of the values in an array.
      +
      static doublecalcMaximum(double[] values) +
      Calculates the maximum of the values in an array.
      +
      static intcalcMaximum(int[] values) +
      Calculates the maximum of the values in an array.
      +
      static doublecalcMedian(double[] values) +
      Calculates the median of the values in an array.
      +
      static doublecalcMinimum(double[] values) +
      Calculates the minimum of the values in an array.
      +
      static intcalcMinimum(int[] values) +
      Calculates the minimum of the values in an array.
      +
      static doublecalcSum(double[] values) +
      Calculates the sum of the values in an array.
      +
      static doublecalcSum(double[] values, + int beginIndex, + int endIndex) +
      Calculates the sum of the values in an array, considering only array + elements within a specified range.
      +
      static double[]createDoubleArrayOfOnes(int nElements) +
      Creates a double array of ones.
      +
      static double[]createDoubleArrayOfRandomNumbers(int nElements) +
      Creates a double array of random numbers.
      +
      static double[]createDoubleArrayOfRandomNumbers(int nElements, + java.util.Random random) +
      Creates a double array of random numbers.
      +
      static int[]generateRandomPermutation(int nElements) +
      Generates a random permutation.
      +
      static int[]generateRandomPermutation(int nElements, + java.util.Random random) +
      Generates a random permutation.
      +
      +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        calcSum

        +
        public static double calcSum(double[] values)
        +
        Calculates the sum of the values in an array.
        +
        +
        Parameters:
        +
        values - Values
        +
        Returns:
        +
        Sum of values
        +
        +
      • +
      + + + +
        +
      • +

        calcSum

        +
        public static double calcSum(double[] values,
        +                             int beginIndex,
        +                             int endIndex)
        +
        Calculates the sum of the values in an array, considering only array + elements within a specified range. + +

        + The sum is calculated over the elements + values[beginIndex], ..., values[endIndex - 1]. +

        +
        +
        Parameters:
        +
        values - Values
        +
        beginIndex - Begin index
        +
        endIndex - End index
        +
        Returns:
        +
        Sum of values
        +
        +
      • +
      + + + +
        +
      • +

        calcAverage

        +
        public static double calcAverage(double[] values)
        +
        Calculates the average of the values in an array.
        +
        +
        Parameters:
        +
        values - Values
        +
        Returns:
        +
        Average value
        +
        +
      • +
      + + + +
        +
      • +

        calcMedian

        +
        public static double calcMedian(double[] values)
        +
        Calculates the median of the values in an array.
        +
        +
        Parameters:
        +
        values - Values
        +
        Returns:
        +
        Median value
        +
        +
      • +
      + + + +
        +
      • +

        calcMinimum

        +
        public static double calcMinimum(double[] values)
        +
        Calculates the minimum of the values in an array.
        +
        +
        Parameters:
        +
        values - Values
        +
        Returns:
        +
        Minimum value
        +
        +
      • +
      + + + +
        +
      • +

        calcMaximum

        +
        public static double calcMaximum(double[] values)
        +
        Calculates the maximum of the values in an array.
        +
        +
        Parameters:
        +
        values - Values
        +
        Returns:
        +
        Maximum value
        +
        +
      • +
      + + + +
        +
      • +

        calcMinimum

        +
        public static int calcMinimum(int[] values)
        +
        Calculates the minimum of the values in an array.
        +
        +
        Parameters:
        +
        values - Values
        +
        Returns:
        +
        Minimum value
        +
        +
      • +
      + + + +
        +
      • +

        calcMaximum

        +
        public static int calcMaximum(int[] values)
        +
        Calculates the maximum of the values in an array.
        +
        +
        Parameters:
        +
        values - Values
        +
        Returns:
        +
        Maximum value
        +
        +
      • +
      + + + +
        +
      • +

        createDoubleArrayOfOnes

        +
        public static double[] createDoubleArrayOfOnes(int nElements)
        +
        Creates a double array of ones.
        +
        +
        Parameters:
        +
        nElements - Number of elements
        +
        Returns:
        +
        Array of ones
        +
        +
      • +
      + + + +
        +
      • +

        createDoubleArrayOfRandomNumbers

        +
        public static double[] createDoubleArrayOfRandomNumbers(int nElements)
        +
        Creates a double array of random numbers.
        +
        +
        Parameters:
        +
        nElements - Number of elements
        +
        Returns:
        +
        Array of random numbers
        +
        +
      • +
      + + + +
        +
      • +

        createDoubleArrayOfRandomNumbers

        +
        public static double[] createDoubleArrayOfRandomNumbers(int nElements,
        +                                                        java.util.Random random)
        +
        Creates a double array of random numbers.
        +
        +
        Parameters:
        +
        nElements - Number of elements
        +
        random - Random number generator
        +
        Returns:
        +
        Array of random numbers
        +
        +
      • +
      + + + +
        +
      • +

        generateRandomPermutation

        +
        public static int[] generateRandomPermutation(int nElements)
        +
        Generates a random permutation. + +

        + A random permutation is generated of the integers + 0, ..., nElements - 1. +

        +
        +
        Parameters:
        +
        nElements - Number of elements
        +
        Returns:
        +
        Random permutation
        +
        +
      • +
      + + + +
        +
      • +

        generateRandomPermutation

        +
        public static int[] generateRandomPermutation(int nElements,
        +                                              java.util.Random random)
        +
        Generates a random permutation. + +

        + A random permutation is generated of the integers + 0, ..., nElements - 1. +

        +
        +
        Parameters:
        +
        nElements - Number of elements
        +
        random - Random number generator
        +
        Returns:
        +
        Random permutation
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/util/DynamicDoubleArray.html b/docs/cwts/util/DynamicDoubleArray.html new file mode 100644 index 0000000..b3af8d0 --- /dev/null +++ b/docs/cwts/util/DynamicDoubleArray.html @@ -0,0 +1,580 @@ + + + + + +DynamicDoubleArray + + + + + + + + +
+ + + + +
+ + +
+
Package cwts.util
+

Class DynamicDoubleArray

+
+
+
    +
  • java.lang.Object
  • +
  • +
      +
    • cwts.util.DynamicDoubleArray
    • +
    +
  • +
+
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    java.lang.Cloneable
    +
    +
    +
    +
    public class DynamicDoubleArray
    +extends java.lang.Object
    +implements java.lang.Cloneable
    +
    Dynamic array of doubles.
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Summary

      + + + + + + + + + + + + + + +
      Fields 
      Modifier and TypeField and Description
      static intDEFAULT_INITIAL_CAPACITY +
      Default initial capacity of the array.
      +
      static doubleRELATIVE_CAPACITY_INCREASE +
      Relative increase in the capacity of the array when the existing + capacity is insufficient.
      +
      +
    • +
    + +
      +
    • + + +

      Constructor Summary

      + + + + + + + + + + + + + + +
      Constructors 
      Constructor and Description
      DynamicDoubleArray() +
      Constructs an empty dynamic array.
      +
      DynamicDoubleArray(double[] values) +
      Constructs a dynamic array containing specified values.
      +
      DynamicDoubleArray(int initialCapacity) +
      Constructs an empty dynamic array with a specified initial capacity.
      +
      +
    • +
    + +
      +
    • + + +

      Method Summary

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      All Methods Instance Methods Concrete Methods 
      Modifier and TypeMethod and Description
      voidappend(double value) +
      Appends a specified value to the end of the array.
      +
      intcapacity() +
      Returns the capacity of the array.
      +
      voidclear() +
      Removes all elements from the array.
      +
      DynamicDoubleArrayclone() +
      Clones the array.
      +
      voidensureCapacity(int minCapacity) +
      Ensures a specified minimum capacity of the array.
      +
      doubleget(int index) +
      Returns the value of an element of the array.
      +
      voidset(int index, + double value) +
      Sets an element of the array to a specified value.
      +
      intsize() +
      Returns the number of elements of the array.
      +
      voidsort() +
      Sorts the array.
      +
      double[]toArray() +
      Returns a static array copy.
      +
      +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        DEFAULT_INITIAL_CAPACITY

        +
        public static final int DEFAULT_INITIAL_CAPACITY
        +
        Default initial capacity of the array.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        RELATIVE_CAPACITY_INCREASE

        +
        public static final double RELATIVE_CAPACITY_INCREASE
        +
        Relative increase in the capacity of the array when the existing + capacity is insufficient.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        DynamicDoubleArray

        +
        public DynamicDoubleArray()
        +
        Constructs an empty dynamic array.
        +
      • +
      + + + +
        +
      • +

        DynamicDoubleArray

        +
        public DynamicDoubleArray(double[] values)
        +
        Constructs a dynamic array containing specified values.
        +
        +
        Parameters:
        +
        values - Values
        +
        +
      • +
      + + + +
        +
      • +

        DynamicDoubleArray

        +
        public DynamicDoubleArray(int initialCapacity)
        +
        Constructs an empty dynamic array with a specified initial capacity.
        +
        +
        Parameters:
        +
        initialCapacity - Initial capacity
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        clone

        +
        public DynamicDoubleArray clone()
        +
        Clones the array.
        +
        +
        Overrides:
        +
        clone in class java.lang.Object
        +
        Returns:
        +
        Cloned array
        +
        +
      • +
      + + + +
        +
      • +

        capacity

        +
        public int capacity()
        +
        Returns the capacity of the array.
        +
        +
        Returns:
        +
        Capacity
        +
        +
      • +
      + + + +
        +
      • +

        ensureCapacity

        +
        public void ensureCapacity(int minCapacity)
        +
        Ensures a specified minimum capacity of the array. + +

        + The capacity is increased (if necessary) to ensure that it equals at + least the specified minimum capacity. +

        +
        +
        Parameters:
        +
        minCapacity - Minimum capacity
        +
        +
      • +
      + + + +
        +
      • +

        size

        +
        public int size()
        +
        Returns the number of elements of the array.
        +
        +
        Returns:
        +
        Size
        +
        +
      • +
      + + + +
        +
      • +

        get

        +
        public double get(int index)
        +
        Returns the value of an element of the array.
        +
        +
        Parameters:
        +
        index - Index
        +
        Returns:
        +
        Value
        +
        +
      • +
      + + + +
        +
      • +

        set

        +
        public void set(int index,
        +                double value)
        +
        Sets an element of the array to a specified value.
        +
        +
        Parameters:
        +
        index - Index
        +
        value - Value
        +
        +
      • +
      + + + +
        +
      • +

        append

        +
        public void append(double value)
        +
        Appends a specified value to the end of the array.
        +
        +
        Parameters:
        +
        value - Value
        +
        +
      • +
      + + + +
        +
      • +

        clear

        +
        public void clear()
        +
        Removes all elements from the array.
        +
      • +
      + + + +
        +
      • +

        sort

        +
        public void sort()
        +
        Sorts the array.
        +
      • +
      + + + +
        +
      • +

        toArray

        +
        public double[] toArray()
        +
        Returns a static array copy.
        +
        +
        Returns:
        +
        Array
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/util/DynamicIntArray.html b/docs/cwts/util/DynamicIntArray.html new file mode 100644 index 0000000..1633e94 --- /dev/null +++ b/docs/cwts/util/DynamicIntArray.html @@ -0,0 +1,580 @@ + + + + + +DynamicIntArray + + + + + + + + +
+ + + + +
+ + +
+
Package cwts.util
+

Class DynamicIntArray

+
+
+
    +
  • java.lang.Object
  • +
  • +
      +
    • cwts.util.DynamicIntArray
    • +
    +
  • +
+
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    java.lang.Cloneable
    +
    +
    +
    +
    public class DynamicIntArray
    +extends java.lang.Object
    +implements java.lang.Cloneable
    +
    Dynamic array of integers.
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Summary

      + + + + + + + + + + + + + + +
      Fields 
      Modifier and TypeField and Description
      static intDEFAULT_INITIAL_CAPACITY +
      Default initial capacity of the array.
      +
      static doubleRELATIVE_CAPACITY_INCREASE +
      Relative increase in the capacity of the array when the existing + capacity is insufficient.
      +
      +
    • +
    + +
      +
    • + + +

      Constructor Summary

      + + + + + + + + + + + + + + +
      Constructors 
      Constructor and Description
      DynamicIntArray() +
      Constructs an empty dynamic array.
      +
      DynamicIntArray(int initialCapacity) +
      Constructs an empty dynamic array with a specified initial capacity.
      +
      DynamicIntArray(int[] values) +
      Constructs a dynamic array containing specified values.
      +
      +
    • +
    + +
      +
    • + + +

      Method Summary

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      All Methods Instance Methods Concrete Methods 
      Modifier and TypeMethod and Description
      voidappend(int value) +
      Appends a specified value to the end of the array.
      +
      intcapacity() +
      Returns the capacity of the array.
      +
      voidclear() +
      Removes all elements from the array.
      +
      DynamicIntArrayclone() +
      Clones the array.
      +
      voidensureCapacity(int minCapacity) +
      Ensures a specified minimum capacity of the array.
      +
      intget(int index) +
      Returns the value of an element of the array.
      +
      voidset(int index, + int value) +
      Sets an element of the array to a specified value.
      +
      intsize() +
      Returns the number of elements of the array.
      +
      voidsort() +
      Sorts the array.
      +
      int[]toArray() +
      Returns a static array copy.
      +
      +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Field Detail

      + + + +
        +
      • +

        DEFAULT_INITIAL_CAPACITY

        +
        public static final int DEFAULT_INITIAL_CAPACITY
        +
        Default initial capacity of the array.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      + + + +
        +
      • +

        RELATIVE_CAPACITY_INCREASE

        +
        public static final double RELATIVE_CAPACITY_INCREASE
        +
        Relative increase in the capacity of the array when the existing + capacity is insufficient.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        DynamicIntArray

        +
        public DynamicIntArray()
        +
        Constructs an empty dynamic array.
        +
      • +
      + + + +
        +
      • +

        DynamicIntArray

        +
        public DynamicIntArray(int initialCapacity)
        +
        Constructs an empty dynamic array with a specified initial capacity.
        +
        +
        Parameters:
        +
        initialCapacity - Initial capacity
        +
        +
      • +
      + + + +
        +
      • +

        DynamicIntArray

        +
        public DynamicIntArray(int[] values)
        +
        Constructs a dynamic array containing specified values.
        +
        +
        Parameters:
        +
        values - Values
        +
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        clone

        +
        public DynamicIntArray clone()
        +
        Clones the array.
        +
        +
        Overrides:
        +
        clone in class java.lang.Object
        +
        Returns:
        +
        Cloned array
        +
        +
      • +
      + + + +
        +
      • +

        capacity

        +
        public int capacity()
        +
        Returns the capacity of the array.
        +
        +
        Returns:
        +
        Capacity
        +
        +
      • +
      + + + +
        +
      • +

        ensureCapacity

        +
        public void ensureCapacity(int minCapacity)
        +
        Ensures a specified minimum capacity of the array. + +

        + The capacity is increased (if necessary) to ensure that it equals at + least the specified minimum capacity. +

        +
        +
        Parameters:
        +
        minCapacity - Minimum capacity
        +
        +
      • +
      + + + +
        +
      • +

        size

        +
        public int size()
        +
        Returns the number of elements of the array.
        +
        +
        Returns:
        +
        Size
        +
        +
      • +
      + + + +
        +
      • +

        get

        +
        public int get(int index)
        +
        Returns the value of an element of the array.
        +
        +
        Parameters:
        +
        index - Index
        +
        Returns:
        +
        Value
        +
        +
      • +
      + + + +
        +
      • +

        set

        +
        public void set(int index,
        +                int value)
        +
        Sets an element of the array to a specified value.
        +
        +
        Parameters:
        +
        index - Index
        +
        value - Value
        +
        +
      • +
      + + + +
        +
      • +

        append

        +
        public void append(int value)
        +
        Appends a specified value to the end of the array.
        +
        +
        Parameters:
        +
        value - Value
        +
        +
      • +
      + + + +
        +
      • +

        clear

        +
        public void clear()
        +
        Removes all elements from the array.
        +
      • +
      + + + +
        +
      • +

        sort

        +
        public void sort()
        +
        Sorts the array.
        +
      • +
      + + + +
        +
      • +

        toArray

        +
        public int[] toArray()
        +
        Returns a static array copy.
        +
        +
        Returns:
        +
        Array
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/util/FastMath.html b/docs/cwts/util/FastMath.html new file mode 100644 index 0000000..1ccf556 --- /dev/null +++ b/docs/cwts/util/FastMath.html @@ -0,0 +1,282 @@ + + + + + +FastMath + + + + + + + + +
+ + + + +
+ + +
+
Package cwts.util
+

Class FastMath

+
+
+
    +
  • java.lang.Object
  • +
  • +
      +
    • cwts.util.FastMath
    • +
    +
  • +
+
+
    +
  • +
    +
    +
    public final class FastMath
    +extends java.lang.Object
    +
    Fast implementations of mathematical functions. + +

    + All methods in this class are static. +

    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Method Summary

      + + + + + + + + + + + + + + +
      All Methods Static Methods Concrete Methods 
      Modifier and TypeMethod and Description
      static doublefastExp(double exponent) +
      Calculates exp(exponent) using a fast implementation.
      +
      static doublefastPow(double base, + int exponent) +
      Calculates base ^ exponent using a fast implementation.
      +
      +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        fastExp

        +
        public static double fastExp(double exponent)
        +
        Calculates exp(exponent) using a fast implementation.
        +
        +
        Parameters:
        +
        exponent - Exponent
        +
        Returns:
        +
        exp(exponent)
        +
        +
      • +
      + + + +
        +
      • +

        fastPow

        +
        public static double fastPow(double base,
        +                             int exponent)
        +
        Calculates base ^ exponent using a fast implementation.
        +
        +
        Parameters:
        +
        base - Base
        +
        exponent - Exponent
        +
        Returns:
        +
        base ^ exponent
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + + + + + diff --git a/docs/cwts/util/package-frame.html b/docs/cwts/util/package-frame.html new file mode 100644 index 0000000..d74aca3 --- /dev/null +++ b/docs/cwts/util/package-frame.html @@ -0,0 +1,24 @@ + + + + + +cwts.util + + + + + + +

cwts.util

+ + + diff --git a/docs/cwts/util/package-summary.html b/docs/cwts/util/package-summary.html new file mode 100644 index 0000000..195c115 --- /dev/null +++ b/docs/cwts/util/package-summary.html @@ -0,0 +1,177 @@ + + + + + +cwts.util + + + + + + + + +
+ + + + +
+ +
+

Package cwts.util

+
+
Provides miscellaneous utility classes.
+
+

See: Description

+
+
+
    +
  • + + + + + + + + + + + + + + + + + + + + + + + + +
    Class Summary 
    ClassDescription
    Arrays +
    Utility functions for arrays.
    +
    DynamicDoubleArray +
    Dynamic array of doubles.
    +
    DynamicIntArray +
    Dynamic array of integers.
    +
    FastMath +
    Fast implementations of mathematical functions.
    +
    +
  • +
+ + + +

Package cwts.util Description

+
Provides miscellaneous utility classes.
+
+ + + + + + diff --git a/docs/cwts/util/package-tree.html b/docs/cwts/util/package-tree.html new file mode 100644 index 0000000..8a3ac32 --- /dev/null +++ b/docs/cwts/util/package-tree.html @@ -0,0 +1,146 @@ + + + + + +cwts.util Class Hierarchy + + + + + + + + +
+ + + + +
+ +
+

Hierarchy For Package cwts.util

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +
+ + + + + + diff --git a/docs/deprecated-list.html b/docs/deprecated-list.html new file mode 100644 index 0000000..c6325d0 --- /dev/null +++ b/docs/deprecated-list.html @@ -0,0 +1,130 @@ + + + + + +Deprecated List + + + + + + + + +
+ +
+ + + + + + + +
+ + +
+ +
+

Deprecated API

+

Contents

+
+ +
+ + + + + + + +
+ + + + diff --git a/docs/help-doc.html b/docs/help-doc.html new file mode 100644 index 0000000..50c3b24 --- /dev/null +++ b/docs/help-doc.html @@ -0,0 +1,227 @@ + + + + + +API Help + + + + + + + + +
+ +
+ + + + + + + +
+ + +
+ +
+

How This API Document Is Organized

+
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
    +
  • +

    Overview

    +

    The Overview page is the front page of this API document and provides a list of all packages with a summary for each. This page can also contain an overall description of the set of packages.

    +
  • +
  • +

    Package

    +

    Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:

    +
      +
    • Interfaces (italic)
    • +
    • Classes
    • +
    • Enums
    • +
    • Exceptions
    • +
    • Errors
    • +
    • Annotation Types
    • +
    +
  • +
  • +

    Class/Interface

    +

    Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:

    +
      +
    • Class inheritance diagram
    • +
    • Direct Subclasses
    • +
    • All Known Subinterfaces
    • +
    • All Known Implementing Classes
    • +
    • Class/interface declaration
    • +
    • Class/interface description
    • +
    +
      +
    • Nested Class Summary
    • +
    • Field Summary
    • +
    • Constructor Summary
    • +
    • Method Summary
    • +
    +
      +
    • Field Detail
    • +
    • Constructor Detail
    • +
    • Method Detail
    • +
    +

    Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.

    +
  • +
  • +

    Annotation Type

    +

    Each annotation type has its own separate page with the following sections:

    +
      +
    • Annotation Type declaration
    • +
    • Annotation Type description
    • +
    • Required Element Summary
    • +
    • Optional Element Summary
    • +
    • Element Detail
    • +
    +
  • +
  • +

    Enum

    +

    Each enum has its own separate page with the following sections:

    +
      +
    • Enum declaration
    • +
    • Enum description
    • +
    • Enum Constant Summary
    • +
    • Enum Constant Detail
    • +
    +
  • +
  • +

    Tree (Class Hierarchy)

    +

    There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.

    +
      +
    • When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
    • +
    • When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
    • +
    +
  • +
  • +

    Deprecated API

    +

    The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.

    +
  • +
  • +

    Prev/Next

    +

    These links take you to the next or previous class, interface, package, or related page.

    +
  • +
  • +

    Frames/No Frames

    +

    These links show and hide the HTML frames. All pages are available with or without frames.

    +
  • +
  • +

    All Classes

    +

    The All Classes link shows all classes and interfaces except non-static nested types.

    +
  • +
  • +

    Serialized Form

    +

    Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.

    +
  • +
  • +

    Constant Field Values

    +

    The Constant Field Values page lists the static final fields and their values.

    +
  • +
+This help file applies to API documentation generated using the standard doclet.
+ +
+ + + + + + + +
+ + + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..cebf26d --- /dev/null +++ b/docs/index.html @@ -0,0 +1,77 @@ + + + + + +Generated Documentation (Untitled) + + + + + +
+
+
+ +
+
+ +
+
+
+ +
+
+ + diff --git a/docs/overview-frame.html b/docs/overview-frame.html new file mode 100644 index 0000000..a80a0ac --- /dev/null +++ b/docs/overview-frame.html @@ -0,0 +1,27 @@ + + + + + +Overview List + + + + + + +
+ +
+
+

Packages

+ +
+

 

+ + diff --git a/docs/overview-summary.html b/docs/overview-summary.html new file mode 100644 index 0000000..db8301c --- /dev/null +++ b/docs/overview-summary.html @@ -0,0 +1,149 @@ + + + + + +Overview + + + + + + + + +
+ +
+ + + + + + + +
+ + +
+ +
+ + + + + + + + + + + + + + + + +
Packages 
PackageDescription
cwts.networkanalysis +
Provides data structures and algorithms for network analysis.
+
cwts.util +
Provides miscellaneous utility classes.
+
+
+ +
+ + + + + + + +
+ + + + diff --git a/docs/overview-tree.html b/docs/overview-tree.html new file mode 100644 index 0000000..0e49b47 --- /dev/null +++ b/docs/overview-tree.html @@ -0,0 +1,176 @@ + + + + + +Class Hierarchy + + + + + + + + +
+ +
+ + + + + + + +
+ + +
+ +
+

Hierarchy For All Packages

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +

Interface Hierarchy

+ +
+ +
+ + + + + + + +
+ + + + diff --git a/docs/package-list b/docs/package-list new file mode 100644 index 0000000..21d4574 --- /dev/null +++ b/docs/package-list @@ -0,0 +1,2 @@ +cwts.networkanalysis +cwts.util diff --git a/docs/script.js b/docs/script.js new file mode 100644 index 0000000..deae5fa --- /dev/null +++ b/docs/script.js @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +var packageSearchIndex; +var typeSearchIndex; +var memberSearchIndex; +var tagSearchIndex; +function loadScripts(doc, tag) { + createElem(doc, tag, 'jquery/jszip/dist/jszip.js'); + createElem(doc, tag, 'jquery/jszip-utils/dist/jszip-utils.js'); + if (window.navigator.userAgent.indexOf('MSIE ') > 0 || window.navigator.userAgent.indexOf('Trident/') > 0 || + window.navigator.userAgent.indexOf('Edge/') > 0) { + createElem(doc, tag, 'jquery/jszip-utils/dist/jszip-utils-ie.js'); + } + createElem(doc, tag, 'search.js'); + + $.get(pathtoroot + "package-search-index.zip") + .done(function() { + JSZipUtils.getBinaryContent(pathtoroot + "package-search-index.zip", function(e, data) { + var zip = new JSZip(data); + zip.load(data); + packageSearchIndex = JSON.parse(zip.file("package-search-index.json").asText()); + }); + }); + $.get(pathtoroot + "type-search-index.zip") + .done(function() { + JSZipUtils.getBinaryContent(pathtoroot + "type-search-index.zip", function(e, data) { + var zip = new JSZip(data); + zip.load(data); + typeSearchIndex = JSON.parse(zip.file("type-search-index.json").asText()); + }); + }); + $.get(pathtoroot + "member-search-index.zip") + .done(function() { + JSZipUtils.getBinaryContent(pathtoroot + "member-search-index.zip", function(e, data) { + var zip = new JSZip(data); + zip.load(data); + memberSearchIndex = JSON.parse(zip.file("member-search-index.json").asText()); + }); + }); + $.get(pathtoroot + "tag-search-index.zip") + .done(function() { + JSZipUtils.getBinaryContent(pathtoroot + "tag-search-index.zip", function(e, data) { + var zip = new JSZip(data); + zip.load(data); + tagSearchIndex = JSON.parse(zip.file("tag-search-index.json").asText()); + }); + }); +} + +function createElem(doc, tag, path) { + var script = doc.createElement(tag); + var scriptElement = doc.getElementsByTagName(tag)[0]; + script.src = pathtoroot + path; + scriptElement.parentNode.insertBefore(script, scriptElement); +} + +function show(type) +{ + count = 0; + for (var key in methods) { + var row = document.getElementById(key); + if ((methods[key] & type) != 0) { + row.style.display = ''; + row.className = (count++ % 2) ? rowColor : altColor; + } + else + row.style.display = 'none'; + } + updateTabs(type); +} + +function updateTabs(type) +{ + for (var value in tabs) { + var sNode = document.getElementById(tabs[value][0]); + var spanNode = sNode.firstChild; + if (value == type) { + sNode.className = activeTableTab; + spanNode.innerHTML = tabs[value][1]; + } + else { + sNode.className = tableTab; + spanNode.innerHTML = "" + tabs[value][1] + ""; + } + } +} diff --git a/docs/serialized-form.html b/docs/serialized-form.html new file mode 100644 index 0000000..42889ae --- /dev/null +++ b/docs/serialized-form.html @@ -0,0 +1,230 @@ + + + + + +Serialized Form + + + + + + + + +
+ + + + +
+ +
+

Serialized Form

+
+
+
    +
  • +

    Package cwts.networkanalysis

    +
      +
    • + + +

      Class cwts.networkanalysis.Clustering extends java.lang.Object implements Serializable

      +
      +
      serialVersionUID:
      +
      1L
      +
      +
        +
      • +

        Serialized Fields

        +
          +
        • +

          clusters

          +
          int[] clusters
          +
          Cluster of each node.
          +
        • +
        • +

          nClusters

          +
          int nClusters
          +
          Number of clusters.
          +
        • +
        • +

          nNodes

          +
          int nNodes
          +
          Number of nodes.
          +
        • +
        +
      • +
      +
    • +
    • + + +

      Class cwts.networkanalysis.Network extends java.lang.Object implements Serializable

      +
      +
      serialVersionUID:
      +
      1L
      +
      +
        +
      • +

        Serialized Fields

        +
          +
        • +

          edgeWeights

          +
          double[] edgeWeights
          +
          Edge weights.
          +
        • +
        • +

          firstNeighborIndices

          +
          int[] firstNeighborIndices
          +
          Index of the first neighbor of each node in the (@code neighbors} array. + +

          + The neighbors of node i are given by + neighbors[firstNeighborIndices[i]], ..., + neighbors[firstNeighborIndices[i + 1] - 1]. +

          +
        • +
        • +

          nEdges

          +
          int nEdges
          +
          Number of edges. + +

          + Each edge is counted twice, once in each direction. +

          +
        • +
        • +

          neighbors

          +
          int[] neighbors
          +
          Neighbors of each node.
          +
        • +
        • +

          nNodes

          +
          int nNodes
          +
          Number of nodes.
          +
        • +
        • +

          nodeWeights

          +
          double[] nodeWeights
          +
          Node weights.
          +
        • +
        • +

          totalEdgeWeightSelfLinks

          +
          double totalEdgeWeightSelfLinks
          +
          Total edge weight of self links.
          +
        • +
        +
      • +
      +
    • +
    +
  • +
+
+ + + + + + diff --git a/docs/stylesheet.css b/docs/stylesheet.css new file mode 100644 index 0000000..eef017b --- /dev/null +++ b/docs/stylesheet.css @@ -0,0 +1,754 @@ +/* Javadoc style sheet */ +/* +Overall document style +*/ + +@import url('resources/fonts/dejavu.css'); + +body { + background-color:#ffffff; + color:#353833; + font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:14px; + margin:0; + padding:0; + height:100%; + width:100%; +} +iframe { + margin:0; + padding:0; + height:100%; + width:100%; + overflow-y:scroll; + border:none; +} +a:link, a:visited { + text-decoration:none; + color:#4A6782; +} +a:hover, a:focus { + text-decoration:none; + color:#bb7a2a; +} +a:active { + text-decoration:none; + color:#4A6782; +} +a[name] { + color:#353833; +} +a[name]:hover { + text-decoration:none; + color:#353833; +} +a[name]:before, a[name]:target { + content:""; + display:block; + height:120px; + margin:-120px 0 0; +} +a[id]:before, a[id]:target { + padding-top:129px; + margin-top:-129px; + color:red; +} +pre { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; +} +h1 { + font-size:20px; +} +h2 { + font-size:18px; +} +h3 { + font-size:16px; + font-style:italic; +} +h4 { + font-size:13px; +} +h5 { + font-size:12px; +} +h6 { + font-size:11px; +} +ul { + list-style-type:disc; +} +code, tt { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; +} +dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; +} +table tr td dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + vertical-align:top; + padding-top:4px; +} +sup { + font-size:8px; +} +/* +Document title and Copyright styles +*/ +.clear { + clear:both; + height:0px; + overflow:hidden; +} +.aboutLanguage { + float:right; + padding:0px 21px; + font-size:11px; + z-index:200; + margin-top:-9px; +} +.legalCopy { + margin-left:.5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color:#FFFFFF; + text-decoration:none; +} +.bar a:hover, .bar a:focus { + color:#bb7a2a; +} +.tab { + background-color:#0066FF; + color:#ffffff; + padding:8px; + width:5em; + font-weight:bold; +} +/* +Navigation bar styles +*/ +.bar { + background-color:#4D7A97; + color:#FFFFFF; + padding:.8em .5em .4em .8em; + height:auto;/*height:1.8em;*/ + font-size:11px; + margin:0; +} +.navPadding { + padding-top: 100px; +} +.fixedNav { + position:fixed; + width:100%; + z-index:999; + background-color:#ffffff; +} +.topNav { + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.bottomNav { + margin-top:10px; + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.subNav { + background-color:#dee3e9; + float:left; + width:100%; + overflow:hidden; + font-size:12px; +} +.subNav div { + clear:left; + float:left; + padding:0 0 5px 6px; + text-transform:uppercase; +} +ul.navList, ul.subNavList { + float:left; + margin:0 25px 0 0; + padding:0; +} +ul.navList li{ + list-style:none; + float:left; + padding: 5px 6px; + text-transform:uppercase; +} +ul.navListSearch { + float:right; + margin:0 0 0 0; + padding:0; +} +ul.navListSearch li { + list-style:none; + float:right; + padding: 5px 6px; + text-transform:uppercase; +} +ul.navListSearch li span { + position:relative; + right:-16px; +} +ul.subNavList li { + list-style:none; + float:left; +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color:#FFFFFF; + text-decoration:none; + text-transform:uppercase; +} +.topNav a:hover, .bottomNav a:hover { + text-decoration:none; + color:#bb7a2a; + text-transform:uppercase; +} +.navBarCell1Rev { + background-color:#F8981D; + color:#253441; + margin: auto 5px; +} +.skipNav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; +} +/* +Page header and footer styles +*/ +.header, .footer { + clear:both; + margin:0 20px; + padding:5px 0 0 0; +} +.indexNav { + margin:10px; + position:relative; +} +.indexNav ul { + padding:0; + margin:0; +} +.indexNav ul li { + display:inline; + list-style-type:none; + padding-right:10px; +} +.indexNav h1 { + font-size:13px; +} +.title { + color:#2c4557; + margin:10px 0; +} +.subTitle { + margin:5px 0 0 0; +} +.header ul { + margin:0 0 15px 0; + padding:0; +} +.footer ul { + margin:20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style:none; + font-size:13px; +} +/* +Heading styles +*/ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding:0; + margin:15px 0; +} +ul.blockList li.blockList h2 { + padding:0px 0 20px 0; +} +/* +Page layout container styles +*/ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + clear:both; + padding:10px 20px; + position:relative; +} +.indexContainer { + margin:10px; + position:relative; + font-size:12px; +} +.indexContainer h2 { + font-size:13px; + padding:0 0 3px 0; +} +.indexContainer ul { + margin:0; + padding:0; +} +.indexContainer ul li { + list-style:none; + padding-top:2px; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size:12px; + font-weight:bold; + margin:10px 0 0 0; + color:#4E4E4E; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + margin:5px 0 10px 0px; + font-size:14px; + font-family:'DejaVu Sans Mono',monospace; +} +.serializedFormContainer dl.nameValue dt { + margin-left:1px; + font-size:1.1em; + display:inline; + font-weight:bold; +} +.serializedFormContainer dl.nameValue dd { + margin:0 0 0 1px; + font-size:1.1em; + display:inline; +} +/* +List styles +*/ +li.circle { + list-style:circle; +} +ul.horizontal li { + display:inline; + font-size:0.9em; +} +ul.inheritance { + margin:0; + padding:0; +} +ul.inheritance li { + display:inline; + list-style:none; +} +ul.inheritance li ul.inheritance { + margin-left:15px; + padding-left:15px; + padding-top:1px; +} +ul.blockList, ul.blockListLast { + margin:10px 0 10px 0; + padding:0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style:none; + margin-bottom:15px; + line-height:1.4; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding:0px 20px 5px 10px; + border:1px solid #ededed; + background-color:#f8f8f8; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding:0 0 5px 8px; + background-color:#ffffff; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left:0; + padding-left:0; + padding-bottom:15px; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style:none; + border-bottom:none; + padding-bottom:0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top:0; + margin-bottom:1px; +} +/* +Table styles +*/ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { + width:100%; + border-spacing:0; + border-left:1px solid #EEE; + border-right:1px solid #EEE; + border-bottom:1px solid #EEE; +} +.overviewSummary, .memberSummary { + padding:0px; +} +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { + position:relative; + text-align:left; + background-repeat:no-repeat; + color:#253441; + font-weight:bold; + clear:none; + overflow:hidden; + padding:0px; + padding-top:10px; + padding-left:1px; + margin:0px; + white-space:pre; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { + color:#FFFFFF; +} +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; + float:left; + background-color:#F8981D; + border: none; + height:16px; +} +.memberSummary caption span.activeTableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#F8981D; + height:16px; +} +.memberSummary caption span.tableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#4D7A97; + height:16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { + padding-top:0px; + padding-left:0px; + padding-right:0px; + background-image:none; + float:none; + display:inline; +} +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { + display:none; + width:5px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .activeTableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .tableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + background-color:#4D7A97; + float:left; + +} +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td { + text-align:left; + padding:0px 0px 12px 10px; +} +th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, +td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; +} +th.colFirst, th.colLast, th.colOne, .constantsSummary th { + background:#dee3e9; + text-align:left; + padding:8px 3px 3px 7px; +} +td.colFirst, th.colFirst { + white-space:nowrap; + font-size:13px; +} +td.colLast, th.colLast { + font-size:13px; +} +td.colOne, th.colOne { + font-size:13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.useSummary td.colFirst, .useSummary th.colFirst, +.overviewSummary td.colOne, .overviewSummary th.colOne, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colOne, .memberSummary th.colOne, +.typeSummary td.colFirst{ + width:25%; + vertical-align:top; +} +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { + font-weight:bold; +} +.tableSubHeadingColor { + background-color:#EEEEFF; +} +.altColor { + background-color:#FFFFFF; +} +.rowColor { + background-color:#EEEEEF; +} +/* +Content styles +*/ +.description pre { + margin-top:0; +} +.deprecatedContent { + margin:0; + padding:10px 0; +} +.docSummary { + padding:0; +} + +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style:normal; +} + +div.block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} + +td.colLast div { + padding-top:0px; +} + + +td.colLast a { + padding-bottom:3px; +} +/* +Formatting effect styles +*/ +.sourceLineNo { + color:green; + padding:0 30px 0 0; +} +h1.hidden { + visibility:hidden; + overflow:hidden; + font-size:10px; +} +.block { + display:block; + margin:3px 10px 2px 0px; + color:#474747; +} +.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, +.moduleLabelInClass, .overrideSpecifyLabel, .packageLabelInClass, +.packageHierarchyLabel, .paramLabel, .returnLabel, .seeLabel, .simpleTagLabel, +.throwsLabel, .typeNameLabel, .typeNameLink, .searchTagLink { + font-weight:bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style:italic; +} + +div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style:normal; +} + +div.contentContainer ul.blockList li.blockList h2 { + padding-bottom:0px; +} +/* +IFRAME specific styles +*/ +.mainContainer { + margin:0 auto; + padding:0; + height:100%; + width:100%; + position:fixed; + top:0; + left:0; +} +.leftContainer { + height:100%; + position:fixed; + width:320px; +} +.leftTop { + position:relative; + float:left; + width:315px; + top:0; + left:0; + height:30%; + border-right:6px solid #ccc; + border-bottom:6px solid #ccc; +} +.leftBottom { + position:relative; + float:left; + width:315px; + bottom:0; + left:0; + height:70%; + border-right:6px solid #ccc; + border-top:1px solid #000; +} +.rightContainer { + position:absolute; + left:320px; + top:0; + bottom:0; + height:100%; + right:0; + border-left:1px solid #000; +} +.rightIframe { + margin:0; + padding:0; + height:100%; + right:30px; + width:100%; + overflow:visible; + margin-bottom:30px; +} +/* +HTML5 specific styles +*/ +main, nav, header, footer, section { + display:block; +} +.ui-autocomplete-category { + font-weight:bold; + font-size:15px; + padding:7px 0 7px 3px; + background-color:#4D7A97; + color:#FFFFFF; +} +.resultItem { + font-size:13px; +} +.ui-autocomplete { + max-height:85%; + max-width:65%; + overflow-y:scroll; + overflow-x:scroll; + white-space:nowrap; + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); +} +ul.ui-autocomplete { + position:fixed; + z-index:999999; +} +ul.ui-autocomplete li { + float:left; + clear:both; + width:100%; +} +.resultHighlight { + font-weight:bold; +} +#search { + background-image:url('resources/glass.png'); + background-size:13px; + background-repeat:no-repeat; + background-position:2px 3px; + padding-left:20px; + position:relative; + right:-18px; +} +#reset { + background-color: rgb(255,255,255); + border:0 none; + width:16px; + height:17px; + position:relative; + left:-2px; + background-image:url('resources/x.png'); + background-repeat:no-repeat; + background-size:12px; + background-position:center; +} +.watermark { + color:#888; +} +.searchTagDescResult { + font-style:italic; + font-size:11px; +} +.searchTagHolderResult { + font-style:italic; + font-size:12px; +} diff --git a/release.sh b/release.sh new file mode 100644 index 0000000..0b64452 --- /dev/null +++ b/release.sh @@ -0,0 +1,20 @@ +#!/bin/bash +VERSION=`git describe --tags` +printf "Building for version $VERSION\n" +printf "Make sure versions match also in documentation and source code.\n" + +printf "\nBuilding java class files\n" +printf "================================================\n" +javac -d build/jar src/cwts/networkanalysis/*.java src/cwts/networkanalysis/run/*.java src/cwts/util/*.java + +printf "\nPackaging jar file\n" +printf "================================================\n" +jar cvfe dist/RunNetworkClustering.jar cwts.networkanalysis.run.RunNetworkClustering -C build/jar . + +printf "\nPackaging source code\n" +printf "================================================\n" +find src -type f -iname "*.java" -print0 | tar -czvf dist/RunNetworkClustering-source.tar.gz --null -T - + +printf "\nBuilding javadoc\n" +printf "================================================\n" +javadoc -noindex -d docs/ src/cwts/networkanalysis/*.java src/cwts/util/*.java diff --git a/src/cwts/networkanalysis/CPMClusteringAlgorithm.java b/src/cwts/networkanalysis/CPMClusteringAlgorithm.java new file mode 100644 index 0000000..b7ef1da --- /dev/null +++ b/src/cwts/networkanalysis/CPMClusteringAlgorithm.java @@ -0,0 +1,138 @@ +package cwts.networkanalysis; + +/** + * Abstract base class for clustering algorithms that use the CPM quality + * function. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public abstract class CPMClusteringAlgorithm implements Cloneable, QualityClusteringAlgorithm +{ + /** + * Default resolution parameter. + */ + public static final double DEFAULT_RESOLUTION = 1; + + /** + * Resolution parameter. + */ + protected double resolution; + + /** + * Constructs a CPM clustering algorithm. + */ + public CPMClusteringAlgorithm() + { + this(DEFAULT_RESOLUTION); + } + + /** + * Constructs a CPM clustering algorithm with a specified resolution + * parameter. + * + * @param resolution Resolution parameter + */ + public CPMClusteringAlgorithm(double resolution) + { + this.resolution = resolution; + } + + /** + * Clones the algorithm. + * + * @return Cloned algorithm + */ + public CPMClusteringAlgorithm clone() + { + try + { + return (CPMClusteringAlgorithm)super.clone(); + } + catch (CloneNotSupportedException e) + { + return null; + } + } + + /** + * Returns the resolution parameter. + * + * @return Resolution parameter + */ + public double getResolution() + { + return resolution; + } + + /** + * Sets the resolution parameter. + * + * @param resolution Resolution parameter + */ + public void setResolution(double resolution) + { + this.resolution = resolution; + } + + /** + * Calculates the quality of a clustering using the CPM quality function. + * + *

+ * The CPM quality function is given by + *

+ * + *
+ * {@code 1 / (2 * m) * sum(d(c[i], c[j]) * (a[i][j] - resolution * n[i] * + * n[j]))}, + *
+ * + *

+ * where {@code a[i][j]} is the weight of the edge between nodes {@code i} + * and {@code j}, {@code n[i]} is the weight of node {@code i}, {@code m} + * is the total edge weight, and {@code resolution} is the resolution + * parameter. The function {@code d(c[i], c[j])} equals 1 if nodes {@code + * i} and {@code j} belong to the same cluster and 0 otherwise. The sum is + * taken over all pairs of nodes {@code i} and {@code j}. + *

+ * + *

+ * Modularity can be expressed in terms of CPM by setting {@code n[i]} + * equal to the total weight of the edges between node {@code i} and its + * neighbors and by rescaling the resolution parameter by {@code 2 * m}. + *

+ * + * @param network Network + * @param clustering Clustering + * + * @return Quality of the clustering + */ + public double calcQuality(Network network, Clustering clustering) + { + double quality; + double[] clusterWeights; + int i, j, k; + + quality = 0; + + for (i = 0; i < network.nNodes; i++) + { + j = clustering.clusters[i]; + for (k = network.firstNeighborIndices[i]; k < network.firstNeighborIndices[i + 1]; k++) + if (clustering.clusters[network.neighbors[k]] == j) + quality += network.edgeWeights[k]; + } + quality += network.totalEdgeWeightSelfLinks; + + clusterWeights = new double[clustering.nClusters]; + for (i = 0; i < network.nNodes; i++) + clusterWeights[clustering.clusters[i]] += network.nodeWeights[i]; + for (i = 0; i < clustering.nClusters; i++) + quality -= clusterWeights[i] * clusterWeights[i] * resolution; + + quality /= 2 * network.getTotalEdgeWeight() + network.totalEdgeWeightSelfLinks; + + return quality; + } +} diff --git a/src/cwts/networkanalysis/Clustering.java b/src/cwts/networkanalysis/Clustering.java new file mode 100644 index 0000000..1bc4c7c --- /dev/null +++ b/src/cwts/networkanalysis/Clustering.java @@ -0,0 +1,498 @@ +package cwts.networkanalysis; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Clustering of the nodes in a network. + * + *

+ * Each node belongs to exactly one cluster. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class Clustering implements Cloneable, Serializable +{ + private static final long serialVersionUID = 1; + + /** + * Number of nodes. + */ + protected int nNodes; + + /** + * Number of clusters. + */ + protected int nClusters; + + /** + * Cluster of each node. + */ + protected int[] clusters; + + /** + * Loads a clustering from a file. + * + * @param filename File from which a clustering is loaded + * + * @return Loaded clustering + * + * @throws ClassNotFoundException Class not found + * @throws IOException Could not read the file + * + * @see #save(String filename) + */ + public static Clustering load(String filename) throws ClassNotFoundException, IOException + { + Clustering clustering; + ObjectInputStream objectInputStream; + + objectInputStream = new ObjectInputStream(new FileInputStream(filename)); + + clustering = (Clustering)objectInputStream.readObject(); + + objectInputStream.close(); + + return clustering; + } + + /** + * Constructs a singleton clustering for a specified number of nodes. + * + * @param nNodes Number of nodes + */ + public Clustering(int nNodes) + { + this.nNodes = nNodes; + initSingletonClustersHelper(); + } + + /** + * Constructs a clustering using a specified cluster for each node. + * + * @param clusters Cluster of each node + */ + public Clustering(int[] clusters) + { + nNodes = clusters.length; + this.clusters = clusters.clone(); + nClusters = cwts.util.Arrays.calcMaximum(clusters) + 1; + } + + /** + * Clones the clustering. + * + * @return Cloned clustering + */ + public Clustering clone() + { + Clustering clonedClustering; + + try + { + clonedClustering = (Clustering)super.clone(); + clonedClustering.clusters = clusters.clone(); + return clonedClustering; + } + catch (CloneNotSupportedException e) + { + return null; + } + } + + /** + * Saves the clustering in a file. + * + * @param filename File in which the clustering is saved + * + * @throws IOException Could not write to the file + * + * @see #load(String filename) + */ + public void save(String filename) throws IOException + { + ObjectOutputStream objectOutputStream; + + objectOutputStream = new ObjectOutputStream(new FileOutputStream(filename)); + + objectOutputStream.writeObject(this); + + objectOutputStream.close(); + } + + /** + * Returns the number of nodes. + * + * @return Number of nodes + */ + public int getNNodes() + { + return nNodes; + } + + /** + * Returns the number of clusters. + * + * @return Number of clusters + */ + public int getNClusters() + { + return nClusters; + } + + /** + * Returns the cluster of each node. + * + * @return Cluster of each node + */ + public int[] getClusters() + { + return clusters.clone(); + } + + /** + * Returns the cluster of a node. + * + * @param node Node + * + * @return Cluster + */ + public int getCluster(int node) + { + return clusters[node]; + } + + /** + * Returns the number of nodes per cluster. + * + * @return Number of nodes per cluster + */ + public int[] getNNodesPerCluster() + { + int i; + int[] nNodesPerCluster; + + nNodesPerCluster = new int[nClusters]; + for (i = 0; i < nNodes; i++) + nNodesPerCluster[clusters[i]]++; + return nNodesPerCluster; + } + + /** + * Returns a list of nodes per cluster. + * + * @return List of nodes per cluster + */ + public int[][] getNodesPerCluster() + { + int i; + int[] nNodesPerCluster; + int[][] nodesPerCluster; + + nodesPerCluster = new int[nClusters][]; + nNodesPerCluster = getNNodesPerCluster(); + for (i = 0; i < nClusters; i++) + { + nodesPerCluster[i] = new int[nNodesPerCluster[i]]; + nNodesPerCluster[i] = 0; + } + for (i = 0; i < nNodes; i++) + { + nodesPerCluster[clusters[i]][nNodesPerCluster[clusters[i]]] = i; + nNodesPerCluster[clusters[i]]++; + } + return nodesPerCluster; + } + + /** + * Assigns a node to a cluster. + * + * @param node Node + * @param cluster Cluster + */ + public void setCluster(int node, int cluster) + { + this.clusters[node] = cluster; + nClusters = Math.max(nClusters, cluster + 1); + } + + /** + * Initializes a singleton clustering. + * + *

+ * Each node {@code i} is assigned to a cluster {@code i}. + *

+ */ + public void initSingletonClusters() + { + initSingletonClustersHelper(); + } + + /** + * Removes empty clusters. + * + *

+ * Clusters are relabeled to follow a strictly consecutive numbering {@code + * 0, ..., nClusters - 1}. + *

+ * + */ + public void removeEmptyClusters() + { + boolean[] nonEmptyClusters; + int i, j; + int[] newClusters; + + nonEmptyClusters = new boolean[nClusters]; + newClusters = new int[nClusters]; + for (i = 0; i < nNodes; i++) + nonEmptyClusters[clusters[i]] = true; + i = 0; + for (j = 0; j < nClusters; j++) + if (nonEmptyClusters[j]) + { + newClusters[j] = i; + i++; + } + nClusters = i; + for (i = 0; i < nNodes; i++) + clusters[i] = newClusters[clusters[i]]; + } + + /** + * Orders the clusters in decreasing order of their number of nodes. + * + * @see #orderClustersByWeight(double[] nodeWeights) + */ + public void orderClustersByNNodes() + { + orderClustersByWeight(cwts.util.Arrays.createDoubleArrayOfOnes(nNodes)); + } + + /** + * Orders the clusters in decreasing order of their total node weight. + * + *

+ * The total node weight of a cluster equals the sum of the weights of the + * nodes belonging to the cluster. + *

+ * + * @param nodeWeights Node weights + * + * @see #orderClustersByNNodes() + */ + public void orderClustersByWeight(double[] nodeWeights) + { + class Cluster implements Comparable + { + + int cluster; + double weight; + + Cluster(int cluster, double weight) + { + this.cluster = cluster; + this.weight = weight; + } + + public int compareTo(Cluster cluster) + { + return (cluster.weight > weight) ? 1 : ((cluster.weight < weight) ? -1 : 0); + } + } + + Cluster[] clusters; + double[] clusterWeights; + int i; + int[] newClusters; + + clusterWeights = new double[nClusters]; + for (i = 0; i < nNodes; i++) + clusterWeights[this.clusters[i]] += nodeWeights[i]; + clusters = new Cluster[nClusters]; + for (i = 0; i < nClusters; i++) + clusters[i] = new Cluster(i, clusterWeights[i]); + + java.util.Arrays.sort(clusters); + + newClusters = new int[nClusters]; + i = 0; + do + { + newClusters[clusters[i].cluster] = i; + i++; + } while ((i < nClusters) && (clusters[i].weight > 0)); + nClusters = i; + for (i = 0; i < nNodes; i++) + this.clusters[i] = newClusters[this.clusters[i]]; + } + + /** + * Merges the clusters based on a clustering of the clusters. + * + * @param clustering Clustering of the clusters + */ + public void mergeClusters(Clustering clustering) + { + int i; + + for (i = 0; i < nNodes; i++) + clusters[i] = clustering.clusters[clusters[i]]; + nClusters = clustering.nClusters; + } + + /** + * Calculates the normalized mutual information relative to another + * clustering. + * + * @param clustering Other clustering + * + * @return Normalized mutual information + */ + public double calcNormalizedMutualInformation(Clustering clustering) + { + ArrayList> nNodesOverlap; + double entropy1, entropy2, mutualInformation, p; + HashMap clusterOverlaps; + int c1, c2, v; + int[] nNodesPerCluster1, nNodesPerCluster2; + Integer overlap; + Iterator> it; + Map.Entry pair; + + nNodesPerCluster1 = getNNodesPerCluster(); + nNodesPerCluster2 = clustering.getNNodesPerCluster(); + + nNodesOverlap = new ArrayList>(); + + entropy1 = 0; + entropy2 = 0; + for (c1 = 0; c1 < nClusters; c1++) + { + nNodesOverlap.add(new HashMap()); + if (nNodesPerCluster1[c1] > 0) + { + p = (double)nNodesPerCluster1[c1] / nNodes; + entropy1 += -p * Math.log(p); + } + } + for (c2 = 0; c2 < clustering.nClusters; c2++) + if (nNodesPerCluster2[c2] > 0) + { + p = (double)nNodesPerCluster2[c2] / nNodes; + entropy2 += -p * Math.log(p); + } + + for (v = 0; v < nNodes; v++) + { + clusterOverlaps = nNodesOverlap.get(clusters[v]); + overlap = clusterOverlaps.get(clustering.clusters[v]); + if (overlap == null) + nNodesOverlap.get(clusters[v]).put(clustering.clusters[v], 1); + else + nNodesOverlap.get(clusters[v]).put(clustering.clusters[v], overlap + 1); + } + + mutualInformation = 0; + for (c1 = 0; c1 < nClusters; c1++) + { + it = nNodesOverlap.get(c1).entrySet().iterator(); + while (it.hasNext()) + { + pair = it.next(); + c2 = (int)pair.getKey(); + mutualInformation += ((double)pair.getValue() / nNodes) * Math.log(nNodes * (double)pair.getValue() / (nNodesPerCluster1[c1] * nNodesPerCluster2[c2])); + } + } + + return 2 * mutualInformation / (entropy1 + entropy2); + } + + /** + * Calculates the variation of information relative to another clustering. + * + * @param clustering Other clustering + * + * @return Variation of information + */ + public double calcVariationOfInformation(Clustering clustering) + { + ArrayList> nNodesOverlap; + double entropy1, entropy2, jointEntropy, p; + HashMap clusterOverlaps; + int c1, c2, v; + int[] nNodesPerCluster1, nNodesPerCluster2; + Integer overlap; + Iterator> it; + Map.Entry pair; + + nNodesPerCluster1 = getNNodesPerCluster(); + nNodesPerCluster2 = clustering.getNNodesPerCluster(); + + nNodesOverlap = new ArrayList>(); + + entropy1 = 0; + entropy2 = 0; + for (c1 = 0; c1 < nClusters; c1++) + { + nNodesOverlap.add(new HashMap()); + if (nNodesPerCluster1[c1] > 0) + { + p = (double)nNodesPerCluster1[c1] / nNodes; + entropy1 += -p * Math.log(p); + } + } + for (c2 = 0; c2 < clustering.nClusters; c2++) + if (nNodesPerCluster2[c2] > 0) + { + p = (double)nNodesPerCluster2[c2] / nNodes; + entropy2 += -p * Math.log(p); + } + + for (v = 0; v < nNodes; v++) + { + clusterOverlaps = nNodesOverlap.get(clusters[v]); + overlap = clusterOverlaps.get(clustering.clusters[v]); + if (overlap == null) + nNodesOverlap.get(clusters[v]).put(clustering.clusters[v], 1); + else + nNodesOverlap.get(clusters[v]).put(clustering.clusters[v], overlap + 1); + } + + jointEntropy = 0; + for (c1 = 0; c1 < nClusters; c1++) + { + it = nNodesOverlap.get(c1).entrySet().iterator(); + while (it.hasNext()) + { + pair = it.next(); + c2 = (int)pair.getKey(); + p = (double)pair.getValue() / nNodes; + jointEntropy += -p * Math.log(p); + } + } + + return 2 * jointEntropy - entropy1 - entropy2; + } + + private void initSingletonClustersHelper() + { + int i; + + clusters = new int[nNodes]; + for (i = 0; i < nNodes; i++) + clusters[i] = i; + nClusters = nNodes; + } +} diff --git a/src/cwts/networkanalysis/ClusteringAlgorithm.java b/src/cwts/networkanalysis/ClusteringAlgorithm.java new file mode 100644 index 0000000..f229aa0 --- /dev/null +++ b/src/cwts/networkanalysis/ClusteringAlgorithm.java @@ -0,0 +1,20 @@ +package cwts.networkanalysis; + +/** + * Interface for clustering algorithms. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public interface ClusteringAlgorithm +{ + /** + * Finds a clustering of the nodes in a network. + * + * @param network Network + * + * @return Clustering + */ + public Clustering findClustering(Network network); +} diff --git a/src/cwts/networkanalysis/ComponentsAlgorithm.java b/src/cwts/networkanalysis/ComponentsAlgorithm.java new file mode 100644 index 0000000..c06a4de --- /dev/null +++ b/src/cwts/networkanalysis/ComponentsAlgorithm.java @@ -0,0 +1,65 @@ +package cwts.networkanalysis; + +/** + * Algorithm for finding the connected components of a network. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class ComponentsAlgorithm implements ClusteringAlgorithm +{ + /** + * Constructs a components algorithm. + */ + public ComponentsAlgorithm() + { + } + + /** + * Finds the connected components of a network. + * + * @param network Network + * + * @return Connected components + */ + public Clustering findClustering(Network network) + { + boolean[] nodesVisited; + int i, j, k, l; + int[] nodes; + + Clustering clustering = new Clustering(network.getNNodes()); + + clustering.nClusters = 0; + nodesVisited = new boolean[network.nNodes]; + nodes = new int[network.nNodes]; + for (i = 0; i < network.nNodes; i++) + if (!nodesVisited[i]) + { + clustering.clusters[i] = clustering.nClusters; + nodesVisited[i] = true; + nodes[0] = i; + j = 1; + k = 0; + do + { + for (l = network.firstNeighborIndices[nodes[k]]; l < network.firstNeighborIndices[nodes[k] + 1]; l++) + if (!nodesVisited[network.neighbors[l]]) + { + clustering.clusters[network.neighbors[l]] = clustering.nClusters; + nodesVisited[network.neighbors[l]] = true; + nodes[j] = network.neighbors[l]; + j++; + } + k++; + } while (k < j); + + clustering.nClusters++; + } + + clustering.orderClustersByNNodes(); + + return clustering; + } +} diff --git a/src/cwts/networkanalysis/FastLocalMovingAlgorithm.java b/src/cwts/networkanalysis/FastLocalMovingAlgorithm.java new file mode 100644 index 0000000..0623bd0 --- /dev/null +++ b/src/cwts/networkanalysis/FastLocalMovingAlgorithm.java @@ -0,0 +1,244 @@ +package cwts.networkanalysis; + +import cwts.util.Arrays; +import java.util.Random; + +/** + * Fast local moving algorithm. + * + *

+ * The fast local moving algorithm first adds all nodes in a network to a + * queue. It then removes a node from the queue. The node is moved to the + * cluster that results in the largest increase in the quality function. If the + * current cluster assignment of the node is already optimal, the node is not + * moved. If the node is moved to a different cluster, the neighbors of the + * node that do not belong to the node's new cluster and that are not yet in + * the queue are added to the queue. The algorithm continues removing nodes + * from the queue until the queue is empty. + *

+ * + *

+ * The fast local moving algorithm provides a fast variant of the {@link + * StandardLocalMovingAlgorithm}. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class FastLocalMovingAlgorithm extends IterativeCPMClusteringAlgorithm +{ + /** + * Random number generator. + */ + protected Random random; + + /** + * Constructs a fast local moving algorithm. + */ + public FastLocalMovingAlgorithm() + { + this(new Random()); + } + + /** + * Constructs a fast local moving algorithm. + * + * @param random Random number generator + */ + public FastLocalMovingAlgorithm(Random random) + { + this(DEFAULT_RESOLUTION, DEFAULT_N_ITERATIONS, random); + } + + /** + * Constructs a fast local moving algorithm for a specified resolution + * parameter and number of iterations. + * + * @param resolution Resolution parameter + * @param nIterations Number of iterations + * @param random Random number generator + */ + public FastLocalMovingAlgorithm(double resolution, int nIterations, Random random) + { + super(resolution, nIterations); + + this.random = random; + } + + /** + * Improves a clustering by performing one iteration of the fast local + * moving algorithm. + * + *

+ * The fast local moving algorithm first adds all nodes in a network to a + * queue. It then removes a node from the queue. The node is moved to the + * cluster that results in the largest increase in the quality function. If + * the current cluster assignment of the node is already optimal, the node + * is not moved. If the node is moved to a different cluster, the neighbors + * of the node that do not belong to the node's new cluster and that are + * not yet in the queue are added to the queue. The algorithm continues + * removing nodes from the queue until the queue is empty. + *

+ * + * @param network Network + * @param clustering Clustering + * + * @return Boolean indicating whether the clustering has been improved + */ + protected boolean improveClusteringOneIteration(Network network, Clustering clustering) + { + boolean update; + boolean[] stableNodes; + double maxQualityValueIncrement, qualityValueIncrement; + double[] clusterWeights, edgeWeightPerCluster; + int bestCluster, currentCluster, i, j, k, l, nNeighboringClusters, nUnstableNodes, nUnusedClusters; + int[] neighboringClusters, nNodesPerCluster, nodeOrder, unusedClusters; + + if (network.nNodes == 1) + return false; + + update = false; + + clusterWeights = new double[network.nNodes]; + nNodesPerCluster = new int[network.nNodes]; + for (i = 0; i < network.nNodes; i++) + { + clusterWeights[clustering.clusters[i]] += network.nodeWeights[i]; + nNodesPerCluster[clustering.clusters[i]]++; + } + + nUnusedClusters = 0; + unusedClusters = new int[network.nNodes - 1]; + for (i = network.nNodes - 1; i >= 0; i--) + if (nNodesPerCluster[i] == 0) + { + unusedClusters[nUnusedClusters] = i; + nUnusedClusters++; + } + + nodeOrder = Arrays.generateRandomPermutation(network.nNodes, random); + + /* + * Iterate over the nodeOrder array in a cyclical manner. When the end + * of the array has been reached, start again from the beginning. The + * queue of nodes that still need to be visited is given by + * nodeOrder[i], ..., nodeOrder[i + nUnstableNodes - 1]. Continue + * iterating until the queue is empty. + */ + edgeWeightPerCluster = new double[network.nNodes]; + neighboringClusters = new int[network.nNodes]; + stableNodes = new boolean[network.nNodes]; + nUnstableNodes = network.nNodes; + i = 0; + do + { + j = nodeOrder[i]; + + currentCluster = clustering.clusters[j]; + + // Remove the currently selected node from its current cluster. + clusterWeights[currentCluster] -= network.nodeWeights[j]; + nNodesPerCluster[currentCluster]--; + if (nNodesPerCluster[currentCluster] == 0) + { + unusedClusters[nUnusedClusters] = currentCluster; + nUnusedClusters++; + } + + /* + * Identify the neighboring clusters of the currently selected + * node, that is, the clusters with which the currently selected + * node is connected. An empty cluster is also included in the set + * of neighboring clusters. In this way, it is always possible that + * the currently selected node will be moved to an empty cluster. + */ + neighboringClusters[0] = unusedClusters[nUnusedClusters - 1]; + nNeighboringClusters = 1; + for (k = network.firstNeighborIndices[j]; k < network.firstNeighborIndices[j + 1]; k++) + { + l = clustering.clusters[network.neighbors[k]]; + if (edgeWeightPerCluster[l] == 0) + { + neighboringClusters[nNeighboringClusters] = l; + nNeighboringClusters++; + } + edgeWeightPerCluster[l] += network.edgeWeights[k]; + } + + /* + * For each neighboring cluster of the currently selected node, + * calculate the increment of the quality function obtained by + * moving the currently selected node to the neighboring cluster. + * Determine the neighboring cluster for which the increment of the + * quality function is largest. The currently selected node will be + * moved to this optimal cluster. In order to guarantee convergence + * of the algorithm, if the old cluster of the currently selected + * node is optimal but there are also other optimal clusters, the + * currently selected node will be moved back to its old cluster. + */ + bestCluster = currentCluster; + maxQualityValueIncrement = edgeWeightPerCluster[currentCluster] - network.nodeWeights[j] * clusterWeights[currentCluster] * resolution; + for (k = 0; k < nNeighboringClusters; k++) + { + l = neighboringClusters[k]; + + qualityValueIncrement = edgeWeightPerCluster[l] - network.nodeWeights[j] * clusterWeights[l] * resolution; + if (qualityValueIncrement > maxQualityValueIncrement) + { + bestCluster = l; + maxQualityValueIncrement = qualityValueIncrement; + } + + edgeWeightPerCluster[l] = 0; + } + + /* + * Move the currently selected node to its new cluster. Update the + * clustering statistics. + */ + clusterWeights[bestCluster] += network.nodeWeights[j]; + nNodesPerCluster[bestCluster]++; + if (bestCluster == unusedClusters[nUnusedClusters - 1]) + nUnusedClusters--; + + /* + * Mark the currently selected node as stable and remove it from + * the queue. + */ + stableNodes[j] = true; + nUnstableNodes--; + + /* + * If the new cluster of the currently selected node is different + * from the old cluster, some further updating of the clustering + * statistics is performed. Also, the neighbors of the currently + * selected node that do not belong to the new cluster are marked + * as unstable and are added to the queue. + */ + if (bestCluster != currentCluster) + { + clustering.clusters[j] = bestCluster; + if (bestCluster >= clustering.nClusters) + clustering.nClusters = bestCluster + 1; + + for (k = network.firstNeighborIndices[j]; k < network.firstNeighborIndices[j + 1]; k++) + if (stableNodes[network.neighbors[k]] && (clustering.clusters[network.neighbors[k]] != bestCluster)) + { + stableNodes[network.neighbors[k]] = false; + nUnstableNodes++; + nodeOrder[(i + nUnstableNodes < network.nNodes) ? (i + nUnstableNodes) : (i + nUnstableNodes - network.nNodes)] = network.neighbors[k]; + } + + update = true; + } + + i = (i < network.nNodes - 1) ? (i + 1) : 0; + } while (nUnstableNodes > 0); + + if (update) + clustering.removeEmptyClusters(); + + return update; + } +} diff --git a/src/cwts/networkanalysis/IncrementalCPMClusteringAlgorithm.java b/src/cwts/networkanalysis/IncrementalCPMClusteringAlgorithm.java new file mode 100644 index 0000000..3177dfe --- /dev/null +++ b/src/cwts/networkanalysis/IncrementalCPMClusteringAlgorithm.java @@ -0,0 +1,53 @@ +package cwts.networkanalysis; + +/** + * Abstract base class for incremental clustering algorithms that use the CPM + * quality function. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public abstract class IncrementalCPMClusteringAlgorithm extends CPMClusteringAlgorithm implements IncrementalClusteringAlgorithm +{ + /** + * Constructs an incremental CPM clustering algorithm. + */ + public IncrementalCPMClusteringAlgorithm() + { + this(DEFAULT_RESOLUTION); + } + + /** + * Constructs an incremental CPM clustering algorithm with a specified + * resolution parameter. + * + * @param resolution Resolution parameter + */ + public IncrementalCPMClusteringAlgorithm(double resolution) + { + super(resolution); + } + + /** + * Finds a clustering of the nodes in a network. + * + *

+ * The clustering is obtained by calling {@link #improveClustering(Network + * network, Clustering clustering)} and by providing a singleton clustering + * as input to this method. + *

+ * + * @param network Network + * + * @return Clustering + */ + public Clustering findClustering(Network network) + { + Clustering clustering; + + clustering = new Clustering(network.getNNodes()); + improveClustering(network, clustering); + return clustering; + } +} diff --git a/src/cwts/networkanalysis/IncrementalClusteringAlgorithm.java b/src/cwts/networkanalysis/IncrementalClusteringAlgorithm.java new file mode 100644 index 0000000..c3c1426 --- /dev/null +++ b/src/cwts/networkanalysis/IncrementalClusteringAlgorithm.java @@ -0,0 +1,22 @@ +package cwts.networkanalysis; + +/** + * Interface for clustering algorithms that are able to improve an existing + * clustering. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public interface IncrementalClusteringAlgorithm extends ClusteringAlgorithm +{ + /** + * Improves a clustering of the nodes in a network. + * + * @param network Network + * @param clustering Clustering + * + * @return Boolean indicating whether the clustering has been improved + */ + public boolean improveClustering(Network network, Clustering clustering); +} diff --git a/src/cwts/networkanalysis/IterativeCPMClusteringAlgorithm.java b/src/cwts/networkanalysis/IterativeCPMClusteringAlgorithm.java new file mode 100644 index 0000000..a02d9d2 --- /dev/null +++ b/src/cwts/networkanalysis/IterativeCPMClusteringAlgorithm.java @@ -0,0 +1,108 @@ +package cwts.networkanalysis; + +/** + * Abstract base class for iterative clustering algorithms that use the CPM + * quality function. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public abstract class IterativeCPMClusteringAlgorithm extends IncrementalCPMClusteringAlgorithm +{ + /** + * Default number of iterations. + */ + public static final int DEFAULT_N_ITERATIONS = 1; + + /** + * Number of iterations. + */ + protected int nIterations; + + /** + * Constructs an iterative CPM clustering algorithm. + */ + public IterativeCPMClusteringAlgorithm() + { + this(DEFAULT_RESOLUTION, DEFAULT_N_ITERATIONS); + } + + /** + * Constructs an iterative CPM clustering algorithm with a specified + * resolution parameter and number of iterations. + * + * @param resolution Resolution parameter + * @param nIterations Number of iterations + */ + public IterativeCPMClusteringAlgorithm(double resolution, int nIterations) + { + super(resolution); + + this.nIterations = nIterations; + } + + /** + * Returns the number of iterations. + * + * @return Number of iterations + */ + public int getNIterations() + { + return nIterations; + } + + /** + * Sets the number of iterations. + * + * @param nIterations Number of iterations + */ + public void setNIterations(int nIterations) + { + this.nIterations = nIterations; + } + + /** + * Improves a clustering of the nodes in a network. + * + *

+ * If the number of iterations {@code nIterations} is positive, the + * clustering is improved by making {@code nIterations} calls to {@link + * #improveClusteringOneIteration(Network network, Clustering clustering)}. + * If {@code nIterations} equals 0, calls to {@link + * #improveClusteringOneIteration(Network network, Clustering clustering)} + * continue to be made until there has been a call that did not result in + * an improvement of the clustering. + *

+ * + * @param network Network + * @param clustering Clustering + * + * @return Boolean indicating whether the clustering has been improved + */ + public boolean improveClustering(Network network, Clustering clustering) + { + boolean update; + int i; + + update = false; + if (nIterations > 0) + for (i = 0; i < nIterations; i++) + update |= improveClusteringOneIteration(network, clustering); + else + while (improveClusteringOneIteration(network, clustering)) + update = true; + return update; + } + + /** + * Improves a clustering by performing one iteration of an iterative + * clustering algorithm. + * + * @param network Network + * @param clustering Clustering + * + * @return Boolean indicating whether the clustering has been improved + */ + protected abstract boolean improveClusteringOneIteration(Network network, Clustering clustering); +} diff --git a/src/cwts/networkanalysis/LeidenAlgorithm.java b/src/cwts/networkanalysis/LeidenAlgorithm.java new file mode 100644 index 0000000..3757195 --- /dev/null +++ b/src/cwts/networkanalysis/LeidenAlgorithm.java @@ -0,0 +1,259 @@ +package cwts.networkanalysis; + +import java.util.Arrays; +import java.util.Random; + +/** + * Leiden algorithm. + * + *

+ * The Leiden algorithm consists of three phases: + *

+ * + *
    + *
  1. local moving of nodes between clusters,
  2. + *
  3. refinement of the clusters,
  4. + *
  5. aggregation of the network based on the refined clusters, using the + * non-refined clusters to create an initial clustering for the aggregate + * network.
  6. + *
+ * + *

+ * These phases are repeated until no further improvements can be made. By + * default, local moving of nodes is performed using the {@link + * FastLocalMovingAlgorithm}. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class LeidenAlgorithm extends IterativeCPMClusteringAlgorithm +{ + /** + * Default randomness parameter. + */ + public static final double DEFAULT_RANDOMNESS = LocalMergingAlgorithm.DEFAULT_RANDOMNESS; + + /** + * Randomness parameter. + */ + protected double randomness; + + /** + * Local moving algorithm. + */ + protected IncrementalCPMClusteringAlgorithm localMovingAlgorithm; + + /** + * Random number generator. + */ + protected Random random; + + /** + * Constructs a Leiden algorithm. + */ + public LeidenAlgorithm() + { + this(new Random()); + } + + /** + * Constructs a Leiden algorithm. + * + * @param random Random number generator + */ + public LeidenAlgorithm(Random random) + { + this(DEFAULT_RESOLUTION, DEFAULT_N_ITERATIONS, DEFAULT_RANDOMNESS, random); + } + + /** + * Constructs a Leiden algorithm for a specified resolution parameter, + * number of iterations, and randomness parameter. + * + * @param resolution Resolution parameter + * @param nIterations Number of iterations + * @param randomness Randomness parameter + * @param random Random number generator + */ + public LeidenAlgorithm(double resolution, int nIterations, double randomness, Random random) + { + this(resolution, nIterations, randomness, new FastLocalMovingAlgorithm(random), random); + } + + /** + * Constructs a Leiden algorithm for a specified resolution parameter, + * number of iterations, randomness parameter, and local moving algorithm. + * + * @param resolution Resolution parameter + * @param nIterations Number of iterations + * @param randomness Randomness parameter + * @param localMovingAlgorithm Local moving algorithm + * @param random Random number generator + */ + public LeidenAlgorithm(double resolution, int nIterations, double randomness, IncrementalCPMClusteringAlgorithm localMovingAlgorithm, Random random) + { + super(resolution, nIterations); + + this.randomness = randomness; + this.random = random; + setLocalMovingAlgorithm(localMovingAlgorithm); + } + + /** + * Clones the algorithm. + * + * @return Cloned algorithm + */ + public LeidenAlgorithm clone() + { + LeidenAlgorithm LeidenAlgorithm; + + LeidenAlgorithm = (LeidenAlgorithm)super.clone(); + LeidenAlgorithm.localMovingAlgorithm = (IncrementalCPMClusteringAlgorithm)localMovingAlgorithm.clone(); + return LeidenAlgorithm; + } + + /** + * Returns the randomness parameter. + * + * @return Randomness parameter + */ + public double getRandomness() + { + return randomness; + } + + /** + * Returns the local moving algorithm. + * + * @return Local moving algorithm + */ + public IncrementalCPMClusteringAlgorithm getLocalMovingAlgorithm() + { + return (IncrementalCPMClusteringAlgorithm)localMovingAlgorithm.clone(); + } + + /** + * Sets the randomness parameter. + * + * @param randomness Randomness parameter + */ + public void setRandomness(double randomness) + { + this.randomness = randomness; + } + + /** + * Sets the local moving algorithm. + * + * @param localMovingAlgorithm Local moving algorithm + */ + public void setLocalMovingAlgorithm(IncrementalCPMClusteringAlgorithm localMovingAlgorithm) + { + this.localMovingAlgorithm = (IncrementalCPMClusteringAlgorithm)localMovingAlgorithm.clone(); + this.localMovingAlgorithm.resolution = resolution; + } + + /** + * Improves a clustering by performing one iteration of the Leiden + * algorithm. + * + *

+ * The Leiden algorithm consists of three phases: + *

+ * + *
    + *
  1. local moving of nodes between clusters,
  2. + *
  3. refinement of the clusters,
  4. + *
  5. aggregation of the network based on the refined clusters, using the + * non-refined clusters to create an initial clustering for the aggregate + * network.
  6. + *
+ * + *

+ * These phases are repeated until no further improvements can be made. + *

+ * + * @param network Network + * @param clustering Clustering + * + * @return Boolean indicating whether the clustering has been improved + */ + protected boolean improveClusteringOneIteration(Network network, Clustering clustering) + { + boolean update; + Clustering clusteringReducedNetwork, clusteringSubnetwork; + int i, j; + int[] clustersReducedNetwork, nNodesPerClusterReducedNetwork; + int[][] nodesPerCluster; + LocalMergingAlgorithm localMergingAlgorithm; + Network reducedNetwork; + Network[] subnetworks; + + // Update the clustering by moving individual nodes between clusters. + update = localMovingAlgorithm.improveClustering(network, clustering); + + /* + * Terminate the algorithm if each node is assigned to its own cluster. + * Otherwise create an aggregate network and recursively apply the + * algorithm to this network. + */ + if (clustering.nClusters < network.nNodes) + { + /* + * Refine the clustering by iterating over the clusters and by + * trying to split up each cluster into multiple clusters. + */ + localMergingAlgorithm = new LocalMergingAlgorithm(resolution, randomness, random); + subnetworks = network.createSubnetworks(clustering); + nodesPerCluster = clustering.getNodesPerCluster(); + clustering.nClusters = 0; + nNodesPerClusterReducedNetwork = new int[subnetworks.length]; + for (i = 0; i < subnetworks.length; i++) + { + clusteringSubnetwork = localMergingAlgorithm.findClustering(subnetworks[i]); + + for (j = 0; j < subnetworks[i].nNodes; j++) + clustering.clusters[nodesPerCluster[i][j]] = clustering.nClusters + clusteringSubnetwork.clusters[j]; + clustering.nClusters += clusteringSubnetwork.nClusters; + nNodesPerClusterReducedNetwork[i] = clusteringSubnetwork.nClusters; + } + + /* + * Create an aggregate network based on the refined clustering of + * the non-aggregate network. + */ + reducedNetwork = network.createReducedNetwork(clustering); + + /* + * Create an initial clustering for the aggregate network based on + * the non-refined clustering of the non-aggregate network. + */ + clustersReducedNetwork = new int[clustering.nClusters]; + i = 0; + for (j = 0; j < nNodesPerClusterReducedNetwork.length; j++) + { + Arrays.fill(clustersReducedNetwork, i, i + nNodesPerClusterReducedNetwork[j], j); + i += nNodesPerClusterReducedNetwork[j]; + } + clusteringReducedNetwork = new Clustering(clustersReducedNetwork); + + /* + * Recursively apply the algorithm to the aggregate network, + * starting from the initial clustering created for this network. + */ + update |= improveClusteringOneIteration(reducedNetwork, clusteringReducedNetwork); + + /* + * Update the clustering of the non-aggregate network so that it + * coincides with the final clustering obtained for the aggregate + * network. + */ + clustering.mergeClusters(clusteringReducedNetwork); + } + + return update; + } +} diff --git a/src/cwts/networkanalysis/LocalMergingAlgorithm.java b/src/cwts/networkanalysis/LocalMergingAlgorithm.java new file mode 100644 index 0000000..4c5d962 --- /dev/null +++ b/src/cwts/networkanalysis/LocalMergingAlgorithm.java @@ -0,0 +1,282 @@ +package cwts.networkanalysis; + +import cwts.util.Arrays; +import cwts.util.FastMath; +import java.util.Random; + +/** + * Local merging algorithm. + * + *

+ * The local merging algorithm starts from a singleton partition. It performs a + * single iteration over the nodes in a network. Each node belonging to a + * singleton cluster is considered for merging with another cluster. This + * cluster is chosen randomly from all clusters that do not result in a + * decrease in the quality function. The larger the increase in the quality + * function, the more likely a cluster is to be chosen. The strength of this + * effect is determined by the randomness parameter. The higher the value of + * the randomness parameter, the stronger the randomness in the choice of a + * cluster. The lower the value of the randomness parameter, the more likely + * the cluster resulting in the largest increase in the quality function is to + * be chosen. A node is merged with a cluster only if both are sufficiently + * well connected to the rest of the network. + *

+ * + *

+ * The local merging algorithm is used in the cluster refinement phase of the + * {@link LeidenAlgorithm}. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class LocalMergingAlgorithm extends CPMClusteringAlgorithm +{ + /** + * Default randomness parameter. + */ + public static final double DEFAULT_RANDOMNESS = 1e-2; + + /** + * Randomness parameter. + */ + protected double randomness; + + /** + * Random number generator. + */ + protected Random random; + + /** + * Constructs a local merging algorithm. + */ + public LocalMergingAlgorithm() + { + this(new Random()); + } + + /** + * Constructs a local merging algorithm. + * + * @param random Random number generator + */ + public LocalMergingAlgorithm(Random random) + { + this(DEFAULT_RESOLUTION, DEFAULT_RANDOMNESS, random); + } + + /** + * Constructs a local merging algorithm for a specified resolution + * parameter and randomness parameter. + * + * @param resolution Resolution parameter + * @param randomness Randomness parameter + * @param random Random number generator + */ + public LocalMergingAlgorithm(double resolution, double randomness, Random random) + { + super(resolution); + this.randomness = randomness; + this.random = random; + } + + /** + * Returns the randomness parameter. + * + * @return Randomness + */ + public double getRandomness() + { + return randomness; + } + + /** + * Sets the randomness parameter. + * + * @param randomness Randomness + */ + public void setRandomness(double randomness) + { + this.randomness = randomness; + } + + /** + * Finds a clustering of the nodes in a network using the local merging + * algorithm. + * + *

+ * The local merging algorithm starts from a singleton partition. It + * performs a single iteration over the nodes in a network. Each node + * belonging to a singleton cluster is considered for merging with another + * cluster. This cluster is chosen randomly from all clusters that do not + * result in a decrease in the quality function. The larger the increase in + * the quality function, the more likely a cluster is to be chosen. The + * strength of this effect is determined by the randomness parameter. The + * higher the value of the randomness parameter, the stronger the + * randomness in the choice of a cluster. The lower the value of the + * randomness parameter, the more likely the cluster resulting in the + * largest increase in the quality function is to be chosen. A node is + * merged with a cluster only if both are sufficiently well connected to + * the rest of the network. + *

+ * + * @param network Network + * + * @return Clustering + */ + public Clustering findClustering(Network network) + { + boolean update; + boolean[] nonSingletonClusters; + double maxQualityValueIncrement, qualityValueIncrement, r, totalNodeWeight, totalTransformedQualityValueIncrement; + double[] clusterWeights, cumTransformedQualityValueIncrementPerCluster, edgeWeightPerCluster, externalEdgeWeightPerCluster; + int bestCluster, chosenCluster, i, j, k, l, max_idx, mid_idx, min_idx, nNeighboringClusters; + int[] neighboringClusters, nodeOrder; + + Clustering clustering = new Clustering(network.nNodes); + + if (network.nNodes == 1) + return clustering; + + update = false; + + totalNodeWeight = network.getTotalNodeWeight(); + clusterWeights = network.getNodeWeights(); + nonSingletonClusters = new boolean[network.nNodes]; + externalEdgeWeightPerCluster = network.getTotalEdgeWeightPerNode(); + + nodeOrder = Arrays.generateRandomPermutation(network.nNodes, random); + + edgeWeightPerCluster = new double[network.nNodes]; + neighboringClusters = new int[network.nNodes]; + cumTransformedQualityValueIncrementPerCluster = new double[network.nNodes]; + for (i = 0; i < network.nNodes; i++) + { + j = nodeOrder[i]; + + /* + * Only nodes belonging to singleton clusters can be moved to a + * different cluster. This guarantees that clusters will never be + * split up. Additionally, only nodes that are well connected with + * the rest of the network are considered for moving. + */ + if (!nonSingletonClusters[j] && (externalEdgeWeightPerCluster[j] >= clusterWeights[j] * (totalNodeWeight - clusterWeights[j]) * resolution)) + { + /* + * Remove the currently selected node from its current cluster. + * This causes the cluster to be empty. + */ + clusterWeights[j] = 0; + externalEdgeWeightPerCluster[j] = 0; + + /* + * Identify the neighboring clusters of the currently selected + * node, that is, the clusters with which the currently + * selected node is connected. The old cluster of the currently + * selected node is also included in the set of neighboring + * clusters. In this way, it is always possible that the + * currently selected node will be moved back to its old + * cluster. + */ + neighboringClusters[0] = j; + nNeighboringClusters = 1; + for (k = network.firstNeighborIndices[j]; k < network.firstNeighborIndices[j + 1]; k++) + { + l = clustering.clusters[network.neighbors[k]]; + if (edgeWeightPerCluster[l] == 0) + { + neighboringClusters[nNeighboringClusters] = l; + nNeighboringClusters++; + } + edgeWeightPerCluster[l] += network.edgeWeights[k]; + } + + /* + * For each neighboring cluster of the currently selected node, + * determine whether the neighboring cluster is well connected + * with the rest of the network. For each neighboring cluster + * that is well connected, calculate the increment of the + * quality function obtained by moving the currently selected + * node to the neighboring cluster. For each neighboring + * cluster for which the increment is non-negative, calculate a + * transformed increment that will determine the probability + * with which the currently selected node is moved to the + * neighboring cluster. + */ + bestCluster = j; + maxQualityValueIncrement = 0; + totalTransformedQualityValueIncrement = 0; + for (k = 0; k < nNeighboringClusters; k++) + { + l = neighboringClusters[k]; + + if (externalEdgeWeightPerCluster[l] >= clusterWeights[l] * (totalNodeWeight - clusterWeights[l]) * resolution) + { + qualityValueIncrement = edgeWeightPerCluster[l] - network.nodeWeights[j] * clusterWeights[l] * resolution; + + if (qualityValueIncrement > maxQualityValueIncrement) + { + bestCluster = l; + maxQualityValueIncrement = qualityValueIncrement; + } + + if (qualityValueIncrement >= 0) + totalTransformedQualityValueIncrement += FastMath.fastExp(qualityValueIncrement / randomness); + } + + cumTransformedQualityValueIncrementPerCluster[k] = totalTransformedQualityValueIncrement; + + edgeWeightPerCluster[l] = 0; + } + + /* + * Determine the neighboring cluster to which the currently + * selected node will be moved. + */ + if (totalTransformedQualityValueIncrement < Double.POSITIVE_INFINITY) + { + r = totalTransformedQualityValueIncrement * random.nextDouble(); + min_idx = -1; + max_idx = nNeighboringClusters + 1; + while (min_idx < max_idx - 1) + { + mid_idx = (min_idx + max_idx) / 2; + if (cumTransformedQualityValueIncrementPerCluster[mid_idx] >= r) + max_idx = mid_idx; + else + min_idx = mid_idx; + } + chosenCluster = neighboringClusters[max_idx]; + } + else + chosenCluster = bestCluster; + + /* + * Move the currently selected node to its new cluster and + * update the clustering statistics. + */ + clusterWeights[chosenCluster] += network.nodeWeights[j]; + + for (k = network.firstNeighborIndices[j]; k < network.firstNeighborIndices[j + 1]; k++) + if (clustering.clusters[network.neighbors[k]] == chosenCluster) + externalEdgeWeightPerCluster[chosenCluster] -= network.edgeWeights[k]; + else + externalEdgeWeightPerCluster[chosenCluster] += network.edgeWeights[k]; + + if (chosenCluster != j) + { + clustering.clusters[j] = chosenCluster; + + nonSingletonClusters[chosenCluster] = true; + update = true; + } + } + } + + if (update) + clustering.removeEmptyClusters(); + + return clustering; + } +} diff --git a/src/cwts/networkanalysis/LouvainAlgorithm.java b/src/cwts/networkanalysis/LouvainAlgorithm.java new file mode 100644 index 0000000..b5743d7 --- /dev/null +++ b/src/cwts/networkanalysis/LouvainAlgorithm.java @@ -0,0 +1,176 @@ +package cwts.networkanalysis; + +import java.util.Random; + +/** + * Louvain algorithm. + * + *

+ * The Louvain algorithm consists of two phases: + *

+ * + *
    + *
  1. local moving of nodes between clusters,
  2. + *
  3. aggregation of the network based on the clusters.
  4. + *
+ * + *

+ * These phases are repeated until no further improvements can be made. By + * default, local moving of nodes is performed using the {@link + * StandardLocalMovingAlgorithm}. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class LouvainAlgorithm extends IterativeCPMClusteringAlgorithm +{ + /** + * Local moving algorithm. + */ + protected IncrementalCPMClusteringAlgorithm localMovingAlgorithm; + + /** + * Constructs a Louvain algorithm. + */ + public LouvainAlgorithm() + { + this(new Random()); + } + + /** + * Constructs a Louvain algorithm. + * + * @param random Random number generator + */ + public LouvainAlgorithm(Random random) + { + this(DEFAULT_RESOLUTION, DEFAULT_N_ITERATIONS, random); + } + + /** + * Constructs a Louvain algorithm for a specified resolution parameter and + * number of iterations. + * + * @param resolution Resolution parameter + * @param nIterations Number of iterations + * @param random Random number generator + */ + public LouvainAlgorithm(double resolution, int nIterations, Random random) + { + this(resolution, nIterations, new StandardLocalMovingAlgorithm(random)); + } + + /** + * Constructs a Louvain algorithm for a specified resolution parameter, + * number of iterations, and local moving algorithm. + * + * @param resolution Resolution parameter + * @param nIterations Number of iterations + * @param localMovingAlgorithm Local moving algorithm + */ + public LouvainAlgorithm(double resolution, int nIterations, IncrementalCPMClusteringAlgorithm localMovingAlgorithm) + { + super(resolution, nIterations); + + setLocalMovingAlgorithm(localMovingAlgorithm); + } + + /** + * Clones the algorithm. + * + * @return Cloned algorithm + */ + public LouvainAlgorithm clone() + { + LouvainAlgorithm LouvainAlgorithm; + + LouvainAlgorithm = (LouvainAlgorithm)super.clone(); + LouvainAlgorithm.localMovingAlgorithm = (IncrementalCPMClusteringAlgorithm)localMovingAlgorithm.clone(); + return LouvainAlgorithm; + } + + /** + * Returns the local moving algorithm. + * + * @return Local moving algorithm + */ + public IncrementalCPMClusteringAlgorithm getLocalMovingAlgorithm() + { + return (IncrementalCPMClusteringAlgorithm)localMovingAlgorithm.clone(); + } + + /** + * Sets the local moving algorithm. + * + * @param localMovingAlgorithm Local moving algorithm + */ + public void setLocalMovingAlgorithm(IncrementalCPMClusteringAlgorithm localMovingAlgorithm) + { + this.localMovingAlgorithm = (IncrementalCPMClusteringAlgorithm)localMovingAlgorithm.clone(); + this.localMovingAlgorithm.resolution = resolution; + } + + /** + * Improves a clustering by performing one iteration of the Louvain + * algorithm. + * + *

+ * The Louvain algorithm consists of two phases: + *

+ * + *
    + *
  1. local moving of nodes between clusters,
  2. + *
  3. aggregation of the network based on the clusters.
  4. + *
+ * + *

+ * These phases are repeated until no further improvements can be made. + *

+ * + * @param network Network + * @param clustering Clustering + * + * @return Boolean indicating whether the clustering has been improved + */ + protected boolean improveClusteringOneIteration(Network network, Clustering clustering) + { + boolean update; + Clustering reducedClustering; + Network reducedNetwork; + + // Update the clustering by moving individual nodes between clusters. + update = localMovingAlgorithm.improveClustering(network, clustering); + + /* + * Terminate the algorithm if each node is assigned to its own cluster. + * Otherwise create an aggregate network and recursively apply the + * algorithm to this network. + */ + if (clustering.nClusters < network.nNodes) + { + /* + * Create an aggregate network based on the clustering of the + * non-aggregate network. + */ + reducedNetwork = network.createReducedNetwork(clustering); + + /* + * Recursively apply the algorithm to the aggregate network, + * starting from a singleton clustering. + */ + reducedClustering = new Clustering(reducedNetwork.getNNodes()); + update |= improveClusteringOneIteration(reducedNetwork, reducedClustering); + + /* + * Update the clustering of the non-aggregate network so that it + * coincides with the final clustering obtained for the aggregate + * network. + */ + clustering.mergeClusters(reducedClustering); + } + + return update; + } +} diff --git a/src/cwts/networkanalysis/Network.java b/src/cwts/networkanalysis/Network.java new file mode 100644 index 0000000..bd4791e --- /dev/null +++ b/src/cwts/networkanalysis/Network.java @@ -0,0 +1,1467 @@ +package cwts.networkanalysis; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Random; + +/** + * Network. + * + *

+ * Weighted nodes and weighted edges are supported. Directed edges are not + * supported. + *

+ * + *

+ * Network objects are immutable. + *

+ * + *

+ * The adjacency matrix of the network is stored in a sparse compressed format. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class Network implements Serializable +{ + private static final long serialVersionUID = 1; + + /** + * Number of nodes. + */ + protected int nNodes; + + /** + * Number of edges. + * + *

+ * Each edge is counted twice, once in each direction. + *

+ */ + protected int nEdges; + + /** + * Node weights. + */ + protected double[] nodeWeights; + + /** + * Index of the first neighbor of each node in the (@code neighbors} array. + * + *

+ * The neighbors of node {@code i} are given by {@code + * neighbors[firstNeighborIndices[i]], ..., + * neighbors[firstNeighborIndices[i + 1] - 1]}. + *

+ */ + protected int[] firstNeighborIndices; + + /** + * Neighbors of each node. + */ + protected int[] neighbors; + + /** + * Edge weights. + */ + protected double[] edgeWeights; + + /** + * Total edge weight of self links. + */ + protected double totalEdgeWeightSelfLinks; + + /** + * Loads a network from a file. + * + * @param filename File from which a network is loaded + * + * @return Loaded network + * + * @throws ClassNotFoundException Class not found + * @throws IOException Could not read the file + * + * @see #save(String filename) + */ + public static Network load(String filename) throws ClassNotFoundException, IOException + { + Network network; + ObjectInputStream objectInputStream; + + objectInputStream = new ObjectInputStream(new FileInputStream(filename)); + + network = (Network)objectInputStream.readObject(); + + objectInputStream.close(); + + return network; + } + + /** + * Constructs a network based on a list of edges. + * + *

+ * The list of edges is provided in the two-dimensional array {@code + * edges}. Edge {@code i} connects nodes {@code edges[0][i]} and {@code + * edges[1][i]}. Edges do not have weights. If {@code sortedEdges} is + * false, the list of edges does not need to be sorted and each edge must + * be included only once. If {@code sortedEdges}is true, the list of edges + * must be sorted and each edge must be included twice, once in each + * direction. + *

+ * + * @param nodeWeights Node weights + * @param edges Edge list + * @param sortedEdges Indicates whether the edge list is sorted + * @param checkIntegrity Indicates whether to check the integrity of the + * network + */ + public Network(double[] nodeWeights, int[][] edges, boolean sortedEdges, boolean checkIntegrity) + { + this(nodeWeights.length, nodeWeights, false, edges, null, sortedEdges, checkIntegrity); + } + + /** + * Constructs a network based on a list of edges. + * + *

+ * The list of edges is provided in the two-dimensional array {@code + * edges}. Edge {@code i} connects nodes {@code edges[0][i]} and {@code + * edges[1][i]} and has weight {@code edgeWeights[i]}. If {@code + * sortedEdges} is false, the list of edges does not need to be sorted and + * each edge must be included only once. If {@code sortedEdges} is true, + * the list of edges must be sorted and each edge must be included twice, + * once in each direction. + *

+ * + * @param nodeWeights Node weights + * @param edges Edge list + * @param edgeWeights Edge weights + * @param sortedEdges Indicates whether the edge list is sorted + * @param checkIntegrity Indicates whether to check the integrity of the + * network + */ + public Network(double[] nodeWeights, int[][] edges, double[] edgeWeights, boolean sortedEdges, boolean checkIntegrity) + { + this(nodeWeights.length, nodeWeights, false, edges, edgeWeights, sortedEdges, checkIntegrity); + } + + /** + * Constructs a network based on a list of neighbors. + * + *

+ * The list of neighbors is provided in the array {@code neighbors}. The + * neighbors of node {@code i} are given by {@code + * neighbors[firstNeighborIndices[i]], ..., + * neighbors[firstNeighborIndices[i + 1] - 1]}. The array {@code + * firstNeighborIndices} must have a length of the number of nodes plus 1. + * The neighbors of a node must be listed in increasing order in the array + * {@code neighbors}. Edges do not have weights. + *

+ * + * @param nodeWeights Node weights + * @param firstNeighborIndices Index of the first neighbor of each node + * @param neighbors Neighbor list + * @param checkIntegrity Indicates whether to check the integrity of + * the network + */ + public Network(double[] nodeWeights, int[] firstNeighborIndices, int[] neighbors, boolean checkIntegrity) + { + this(nodeWeights.length, nodeWeights, false, firstNeighborIndices, neighbors, null, checkIntegrity); + } + + /** + * Constructs a network based on a list of neighbors. + * + *

+ * The list of neighbors is provided in the array {@code neighbors}. The + * neighbors of node {@code i} are given by {@code + * neighbors[firstNeighborIndices[i]], ..., + * neighbors[firstNeighborIndices[i + 1] - 1]}. The array {@code + * firstNeighborIndices} must have a length of the number of nodes plus 1. + * The neighbors of a node must be listed in increasing order in the array + * {@code neighbors}. For each neighbor in the array {@code neighbors}, the + * corresponding edge weight is provided in the array {@code edgeWeights}. + *

+ * + * @param nodeWeights Node weights + * @param firstNeighborIndices Index of the first neighbor of each node + * @param neighbors Neighbor list + * @param edgeWeights Edge weights + * @param checkIntegrity Indicates whether to check the integrity of + * the network + */ + public Network(double[] nodeWeights, int[] firstNeighborIndices, int[] neighbors, double[] edgeWeights, boolean checkIntegrity) + { + this(nodeWeights.length, nodeWeights, false, firstNeighborIndices, neighbors, edgeWeights, checkIntegrity); + } + + /** + * Constructs a network based on a list of edges. + * + *

+ * The list of edges is provided in the two-dimensional array {@code + * edges}. Edge {@code i} connects nodes {@code edges[0][i]} and {@code + * edges[1][i]}. Edges do not have weights. If {@code sortedEdges} is + * false, the list of edges does not need to be sorted and each edge must + * be included only once. If {@code sortedEdges}is true, the list of edges + * must be sorted and each edge must be included twice, once in each + * direction. + *

+ * + *

+ * If {@code setNodeWeightsToTotalEdgeWeights} is false, the weights of the + * nodes are set to 1. If {@code setNodeWeightsToTotalEdgeWeights} is true, + * the weight of a node is set equal to the total weight of the edges + * between the node and its neighbors. + *

+ * + * @param nNodes Number of nodes + * @param setNodeWeightsToTotalEdgeWeights Indicates whether to set node + * weights equal to total edge + * weights + * @param edges Edge list + * @param sortedEdges Indicates whether the edge list + * is sorted + * @param checkIntegrity Indicates whether to check the + * integrity of the network + */ + public Network(int nNodes, boolean setNodeWeightsToTotalEdgeWeights, int[][] edges, boolean sortedEdges, boolean checkIntegrity) + { + this(nNodes, null, setNodeWeightsToTotalEdgeWeights, edges, null, sortedEdges, checkIntegrity); + } + + /** + * Constructs a network based on a list of edges. + * + *

+ * The list of edges is provided in the two-dimensional array {@code + * edges}. Edge {@code i} connects nodes {@code edges[0][i]} and {@code + * edges[1][i]} and has weight {@code edgeWeights[i]}. If {@code + * sortedEdges} is false, the list of edges does not need to be sorted and + * each edge must be included only once. If {@code sortedEdges} is true, + * the list of edges must be sorted and each edge must be included twice, + * once in each direction. + *

+ * + *

+ * If {@code setNodeWeightsToTotalEdgeWeights} is false, the weights of the + * nodes are set to 1. If {@code setNodeWeightsToTotalEdgeWeights} is true, + * the weight of a node is set equal to the total weight of the edges + * between the node and its neighbors. + *

+ * + * @param nNodes Number of nodes + * @param setNodeWeightsToTotalEdgeWeights Indicates whether to set node + * weights equal to total edge + * weights + * @param edges Edge list + * @param edgeWeights Edge weights + * @param sortedEdges Indicates whether the edge list + * is sorted + * @param checkIntegrity Indicates whether to check the + * integrity of the network + */ + public Network(int nNodes, boolean setNodeWeightsToTotalEdgeWeights, int[][] edges, double[] edgeWeights, boolean sortedEdges, boolean checkIntegrity) + { + this(nNodes, null, setNodeWeightsToTotalEdgeWeights, edges, edgeWeights, sortedEdges, checkIntegrity); + } + + /** + * Constructs a network based on a list of neighbors. + * + *

+ * The list of neighbors is provided in the array {@code neighbors}. The + * neighbors of node {@code i} are given by {@code + * neighbors[firstNeighborIndices[i]], ..., + * neighbors[firstNeighborIndices[i + 1] - 1]}. The array {@code + * firstNeighborIndices} must have a length of the number of nodes plus 1. + * The neighbors of a node must be listed in increasing order in the array + * {@code neighbors}. Edges do not have weights. + *

+ * + *

+ * If {@code setNodeWeightsToTotalEdgeWeights} is false, the weights of the + * nodes are set to 1. If {@code setNodeWeightsToTotalEdgeWeights} is true, + * the weight of a node is set equal to the total weight of the edges + * between the node and its neighbors. + *

+ * + * @param nNodes Number of nodes + * @param setNodeWeightsToTotalEdgeWeights Indicates whether to set node + * weights equal to total edge + * weights + * @param firstNeighborIndices Index of the first neighbor of + * each node + * @param neighbors Neighbor list + * @param checkIntegrity Indicates whether to check the + * integrity of the network + */ + public Network(int nNodes, boolean setNodeWeightsToTotalEdgeWeights, int[] firstNeighborIndices, int[] neighbors, boolean checkIntegrity) + { + this(nNodes, null, setNodeWeightsToTotalEdgeWeights, firstNeighborIndices, neighbors, null, checkIntegrity); + } + + /** + * Constructs a network based on a list of neighbors. + * + *

+ * The list of neighbors is provided in the array {@code neighbors}. The + * neighbors of node {@code i} are given by {@code + * neighbors[firstNeighborIndices[i]], ..., + * neighbors[firstNeighborIndices[i + 1] - 1]}. The array {@code + * firstNeighborIndices} must have a length of the number of nodes plus 1. + * The neighbors of a node must be listed in increasing order in the array + * {@code neighbors}. For each neighbor in the array {@code neighbors}, the + * corresponding edge weight is provided in the array {@code edgeWeights}. + *

+ * + *

+ * If {@code setNodeWeightsToTotalEdgeWeights} is false, the weights of the + * nodes are set to 1. If {@code setNodeWeightsToTotalEdgeWeights} is true, + * the weight of a node is set equal to the total weight of the edges + * between the node and its neighbors. + *

+ * + * @param nNodes Number of nodes + * @param setNodeWeightsToTotalEdgeWeights Indicates whether to set node + * weights equal to total edge + * weights + * @param firstNeighborIndices Index of the first neighbor of + * each node + * @param neighbors Neighbor list + * @param edgeWeights Edge weights + * @param checkIntegrity Indicates whether to check the + * integrity of the network + */ + public Network(int nNodes, boolean setNodeWeightsToTotalEdgeWeights, int[] firstNeighborIndices, int[] neighbors, double[] edgeWeights, boolean checkIntegrity) + { + this(nNodes, null, setNodeWeightsToTotalEdgeWeights, firstNeighborIndices, neighbors, edgeWeights, checkIntegrity); + } + + /** + * Saves the network in a file. + * + * @param filename File in which the network is saved + * + * @throws IOException Could not write to the file + * + * @see #load(String filename) + */ + public void save(String filename) throws IOException + { + ObjectOutputStream objectOutputStream; + + objectOutputStream = new ObjectOutputStream(new FileOutputStream(filename)); + + objectOutputStream.writeObject(this); + + objectOutputStream.close(); + } + + /** + * Returns the number of nodes. + * + * @return Number of nodes + */ + public int getNNodes() + { + return nNodes; + } + + /** + * Returns the total node weight. + * + * @return Total node weight + */ + public double getTotalNodeWeight() + { + return cwts.util.Arrays.calcSum(nodeWeights); + } + + /** + * Returns the weight of each node. + * + * @return Weight of each node + */ + public double[] getNodeWeights() + { + return nodeWeights.clone(); + } + + /** + * Returns the weight of a node. + * + * @param node Node + * + * @return Weight + */ + public double getNodeWeight(int node) + { + return nodeWeights[node]; + } + + /** + * Returns the number of edges. + * + *

+ * Each edge is counted only once, even though an edge runs in two + * directions. This means that the number of edges returned by {@link + * #getEdges()} equals twice the number of edges returned by {@link + * #getNEdges()}. + *

+ * + * @return Number of edges + */ + public int getNEdges() + { + return nEdges / 2; + } + + /** + * Returns the number of neighbors per node. + * + * @return Number of neighbors per node + */ + public int[] getNNeighborsPerNode() + { + int i; + int[] nNeighborsPerNode; + + nNeighborsPerNode = new int[nNodes]; + for (i = 0; i < nNodes; i++) + nNeighborsPerNode[i] = firstNeighborIndices[i + 1] - firstNeighborIndices[i]; + return nNeighborsPerNode; + } + + /** + * Returns the number of neighbors of a node. + * + * @param node Node + * + * @return Number of neighbors + */ + public int getNNeighbors(int node) + { + return firstNeighborIndices[node + 1] - firstNeighborIndices[node]; + } + + /** + * Returns the list of edges. + * + *

+ * Each edge is included twice, once in each direction. This means that the + * number of edges returned by {@link #getEdges()} equals twice the number + * of edges returned by {@link #getNEdges()}. + *

+ * + *

+ * The list of edges is returned in a two-dimensional array {@code edges}. + * Edge {@code i} connects nodes {@code edges[0][i]} and {@code + * edges[1][i]}. + *

+ * + * @return List of edges + */ + public int[][] getEdges() + { + int i; + int[][] edges; + + edges = new int[2][]; + edges[0] = new int[nEdges]; + for (i = 0; i < nNodes; i++) + Arrays.fill(edges[0], firstNeighborIndices[i], firstNeighborIndices[i + 1], i); + edges[1] = neighbors.clone(); + return edges; + } + + /** + * Returns a list of neighbors per node. + * + * @return List of neighbors per node + */ + public int[][] getNeighborsPerNode() + { + int i; + int[][] neighborsPerNode; + + neighborsPerNode = new int[nNodes][]; + for (i = 0; i < nNodes; i++) + neighborsPerNode[i] = Arrays.copyOfRange(neighbors, firstNeighborIndices[i], firstNeighborIndices[i + 1]); + return neighborsPerNode; + } + + /** + * Returns the list of neighbors of a node. + * + * @param node Node + * + * @return List of neighbors + */ + public int[] getNeighbors(int node) + { + return Arrays.copyOfRange(neighbors, firstNeighborIndices[node], firstNeighborIndices[node + 1]); + } + + /** + * Returns the total edge weight. + * + *

+ * Each edge is considered only once, even though an edge runs in two + * directions. This means that the sum of the edge weights returned by + * {@link #getEdgeWeights()} equals twice the total edge weight returned by + * {@link #getTotalEdgeWeight()}. + *

+ * + *

+ * Edge weights of self links are not included. + *

+ * + * @return Total edge weight + */ + public double getTotalEdgeWeight() + { + return cwts.util.Arrays.calcSum(edgeWeights) / 2; + } + + /** + * Returns the total edge weight per node. The total edge weight of a node + * equals the sum of the weights of the edges between the node and its + * neighbors. + * + * @return Total edge weight per node + */ + public double[] getTotalEdgeWeightPerNode() + { + return getTotalEdgeWeightPerNodeHelper(); + } + + /** + * Returns the total edge weight of a node. The total edge weight of a node + * equals the sum of the weights of the edges between the node and its + * neighbors. + * + * @param node Node + * + * @return Total edge weight + */ + public double getTotalEdgeWeight(int node) + { + return cwts.util.Arrays.calcSum(edgeWeights, firstNeighborIndices[node], firstNeighborIndices[node + 1]); + } + + /** + * Returns the edge weights. + * + *

+ * Each edge is included twice, once in each direction. This means that the + * sum of the edge weights returned by {@link #getEdgeWeights()} equals + * twice the total edge weight returned by {@link #getTotalEdgeWeight()}. + *

+ * + * @return Edge weights + */ + public double[] getEdgeWeights() + { + return edgeWeights.clone(); + } + + /** + * Returns a list of edge weights per node. These are the weights of the + * edges between a node and its neighbors. + * + * @return List of edge weights per node + */ + public double[][] getEdgeWeightsPerNode() + { + double[][] edgeWeightsPerNode; + int i; + + edgeWeightsPerNode = new double[nNodes][]; + for (i = 0; i < nNodes; i++) + edgeWeightsPerNode[i] = Arrays.copyOfRange(edgeWeights, firstNeighborIndices[i], firstNeighborIndices[i + 1]); + return edgeWeightsPerNode; + } + + /** + * Returns the list of edge weights of a node. These are the weights of the + * edges between the node and its neighbors. + * + * @param node Node + * + * @return List of edge weights + */ + public double[] getEdgeWeights(int node) + { + return Arrays.copyOfRange(edgeWeights, firstNeighborIndices[node], firstNeighborIndices[node + 1]); + } + + /** + * Returns the total edge weight of self links. + * + * @return Total edge weight of self links + */ + public double getTotalEdgeWeightSelfLinks() + { + return totalEdgeWeightSelfLinks; + } + + /** + * Creates a copy of the network, but without node weights. + * + *

+ * Each node is assigned a weight of 1. + *

+ * + * @return Network without node weights + */ + public Network createNetworkWithoutNodeWeights() + { + Network networkWithoutNodeWeights; + + networkWithoutNodeWeights = new Network(); + networkWithoutNodeWeights.nNodes = nNodes; + networkWithoutNodeWeights.nEdges = nEdges; + networkWithoutNodeWeights.nodeWeights = cwts.util.Arrays.createDoubleArrayOfOnes(nNodes); + networkWithoutNodeWeights.firstNeighborIndices = firstNeighborIndices; + networkWithoutNodeWeights.neighbors = neighbors; + networkWithoutNodeWeights.edgeWeights = edgeWeights; + networkWithoutNodeWeights.totalEdgeWeightSelfLinks = totalEdgeWeightSelfLinks; + return networkWithoutNodeWeights; + } + + /** + * Creates a copy of the network, but without edge weights. + * + *

+ * Each edge is assigned a weight of 1. + *

+ * + * @return Network without edge weights + */ + public Network createNetworkWithoutEdgeWeights() + { + Network networkWithoutEdgeWeights; + + networkWithoutEdgeWeights = new Network(); + networkWithoutEdgeWeights.nNodes = nNodes; + networkWithoutEdgeWeights.nEdges = nEdges; + networkWithoutEdgeWeights.nodeWeights = nodeWeights; + networkWithoutEdgeWeights.firstNeighborIndices = firstNeighborIndices; + networkWithoutEdgeWeights.neighbors = neighbors; + networkWithoutEdgeWeights.edgeWeights = cwts.util.Arrays.createDoubleArrayOfOnes(nEdges); + networkWithoutEdgeWeights.totalEdgeWeightSelfLinks = 0; + return networkWithoutEdgeWeights; + } + + /** + * Creates a copy of the network, but without node and edge weights. + * + *

+ * Each node is assigned a weight of 1, and each edge is assigned a weight + * of 1. + *

+ * + * @return Network without node and edge weights + */ + public Network createNetworkWithoutNodeAndEdgeWeights() + { + Network networkWithoutNodeAndEdgeWeights; + + networkWithoutNodeAndEdgeWeights = new Network(); + networkWithoutNodeAndEdgeWeights.nNodes = nNodes; + networkWithoutNodeAndEdgeWeights.nEdges = nEdges; + networkWithoutNodeAndEdgeWeights.nodeWeights = cwts.util.Arrays.createDoubleArrayOfOnes(nNodes); + networkWithoutNodeAndEdgeWeights.firstNeighborIndices = firstNeighborIndices; + networkWithoutNodeAndEdgeWeights.neighbors = neighbors; + networkWithoutNodeAndEdgeWeights.edgeWeights = cwts.util.Arrays.createDoubleArrayOfOnes(nEdges); + networkWithoutNodeAndEdgeWeights.totalEdgeWeightSelfLinks = 0; + return networkWithoutNodeAndEdgeWeights; + } + + /** + * Creates a copy of the network in which the edge weights have been + * normalized using the association strength. + * + *

+ * The normalized weight {@code a'[i][j]} of the edge between nodes {@code + * i} and {@code j} is given by + *

+ * + *
+ * {@code a'[i][j] = a[i][j] / (n[i] * n[j] / (2 * m))}, + *
+ * + *

+ * where {@code a[i][j]} is the non-normalized weight of the edge between + * nodes {@code i} and {@code j}, {@code n[i]} is the weight of node {@code + * i}, and {@code m} is half the total node weight. + *

+ * + *

+ * If each node's weight equals the total weight of the edges between the + * node and its neighbors, the edge weights are normalized by dividing them + * by the expected edge weights in the random configuration model. + *

+ * + *

+ * The node weights are set to 1. + *

+ * + * @return Normalized network + */ + public Network createNormalizedNetworkUsingAssociationStrength() + { + double totalNodeWeight; + int i, j; + Network normalizedNetwork; + + normalizedNetwork = new Network(); + + normalizedNetwork.nNodes = nNodes; + normalizedNetwork.nEdges = nEdges; + normalizedNetwork.nodeWeights = cwts.util.Arrays.createDoubleArrayOfOnes(nNodes); + normalizedNetwork.firstNeighborIndices = firstNeighborIndices; + normalizedNetwork.neighbors = neighbors; + + normalizedNetwork.edgeWeights = new double[nEdges]; + totalNodeWeight = getTotalNodeWeight(); + for (i = 0; i < nNodes; i++) + for (j = firstNeighborIndices[i]; j < firstNeighborIndices[i + 1]; j++) + normalizedNetwork.edgeWeights[j] = edgeWeights[j] / ((nodeWeights[i] * nodeWeights[neighbors[j]]) / totalNodeWeight); + + normalizedNetwork.totalEdgeWeightSelfLinks = 0; + + return normalizedNetwork; + } + + /** + * Creates a copy of the network in which the edge weights have been + * normalized using fractionalization. + * + *

+ * The normalized weight {@code a'[i][j]} of the edge between nodes {@code + * i} and {@code j} is given by + *

+ * + *
+ * {@code a'[i][j] = a[i][j] * (n / n[i] + n / n[j]) / 2}, + *
+ * + *

+ * where {@code a[i][j]} is the non-normalized weight of the edge between + * nodes {@code i} and {@code j}, {@code n[i]} is the weight of node {@code + * i}, and {@code n} is the number of nodes. + *

+ * + *

+ * The node weights are set to 1. + *

+ * + * @return Normalized network + */ + public Network createNormalizedNetworkUsingFractionalization() + { + int i, j; + Network normalizedNetwork; + + normalizedNetwork = new Network(); + + normalizedNetwork.nNodes = nNodes; + normalizedNetwork.nEdges = nEdges; + normalizedNetwork.nodeWeights = cwts.util.Arrays.createDoubleArrayOfOnes(nNodes); + normalizedNetwork.firstNeighborIndices = firstNeighborIndices; + normalizedNetwork.neighbors = neighbors; + + normalizedNetwork.edgeWeights = new double[nEdges]; + for (i = 0; i < nNodes; i++) + for (j = firstNeighborIndices[i]; j < firstNeighborIndices[i + 1]; j++) + normalizedNetwork.edgeWeights[j] = edgeWeights[j] / (2 / (nNodes / nodeWeights[i] + nNodes / nodeWeights[neighbors[j]])); + + normalizedNetwork.totalEdgeWeightSelfLinks = 0; + + return normalizedNetwork; + } + + /** + * Creates a copy of the network that has been pruned in order to have a + * specified maximum number of edges. + * + *

+ * Only the edges with the highest weights are retained in the pruned + * network. In case of ties, the edges to be retained are selected + * randomly. + *

+ * + * @param maxNEdges Maximum number of edges + * + * @return Pruned network + */ + public Network createPrunedNetwork(int maxNEdges) + { + return createPrunedNetwork(maxNEdges, new Random()); + } + + /** + * Creates a copy of the network that has been pruned in order to have a + * specified maximum number of edges. + * + *

+ * Only the edges with the highest weights are retained in the pruned + * network. In case of ties, the edges to be retained are selected + * randomly. + *

+ * + * @param maxNEdges Maximum number of edges + * @param random Random number generator + * + * @return Pruned network + */ + public Network createPrunedNetwork(int maxNEdges, Random random) + { + double edgeWeightThreshold, randomNumberThreshold; + double[] edgeWeights, randomNumbers, randomNumbersEdgesAtThreshold; + int i, j, k, nEdgesAboveThreshold, nEdgesAtThreshold; + Network prunedNetwork; + + maxNEdges *= 2; + + if (maxNEdges >= nEdges) + return this; + + edgeWeights = new double[nEdges / 2]; + i = 0; + for (j = 0; j < nNodes; j++) + { + k = firstNeighborIndices[j]; + while ((k < firstNeighborIndices[j + 1]) && (neighbors[k] < j)) + { + edgeWeights[i] = this.edgeWeights[k]; + i++; + k++; + } + } + Arrays.sort(edgeWeights); + edgeWeightThreshold = edgeWeights[(nEdges - maxNEdges) / 2]; + + nEdgesAboveThreshold = 0; + while (edgeWeights[nEdges / 2 - nEdgesAboveThreshold - 1] > edgeWeightThreshold) + nEdgesAboveThreshold++; + nEdgesAtThreshold = 0; + while ((nEdgesAboveThreshold + nEdgesAtThreshold < nEdges / 2) && (edgeWeights[nEdges / 2 - nEdgesAboveThreshold - nEdgesAtThreshold - 1] == edgeWeightThreshold)) + nEdgesAtThreshold++; + + randomNumbers = cwts.util.Arrays.createDoubleArrayOfRandomNumbers(nNodes * nNodes, random); + + randomNumbersEdgesAtThreshold = new double[nEdgesAtThreshold]; + i = 0; + for (j = 0; j < nNodes; j++) + { + k = firstNeighborIndices[j]; + while ((k < firstNeighborIndices[j + 1]) && (neighbors[k] < j)) + { + if (this.edgeWeights[k] == edgeWeightThreshold) + { + randomNumbersEdgesAtThreshold[i] = getRandomNumber(j, neighbors[k], randomNumbers); + i++; + } + k++; + } + } + Arrays.sort(randomNumbersEdgesAtThreshold); + randomNumberThreshold = randomNumbersEdgesAtThreshold[nEdgesAboveThreshold + nEdgesAtThreshold - maxNEdges / 2]; + + prunedNetwork = new Network(); + + prunedNetwork.nNodes = nNodes; + prunedNetwork.nEdges = maxNEdges; + prunedNetwork.nodeWeights = nodeWeights; + + prunedNetwork.firstNeighborIndices = new int[nNodes + 1]; + prunedNetwork.neighbors = new int[maxNEdges]; + prunedNetwork.edgeWeights = new double[maxNEdges]; + i = 0; + for (j = 0; j < nNodes; j++) + { + for (k = firstNeighborIndices[j]; k < firstNeighborIndices[j + 1]; k++) + if ((this.edgeWeights[k] > edgeWeightThreshold) || ((this.edgeWeights[k] == edgeWeightThreshold) && (getRandomNumber(j, neighbors[k], randomNumbers) >= randomNumberThreshold))) + { + prunedNetwork.neighbors[i] = neighbors[k]; + prunedNetwork.edgeWeights[i] = this.edgeWeights[k]; + i++; + } + prunedNetwork.firstNeighborIndices[j + 1] = i; + } + + prunedNetwork.totalEdgeWeightSelfLinks = 0; + + return prunedNetwork; + } + + /** + * Creates an induced subnetwork for specified nodes. + * + * @param nodes Nodes + * + * @return Subnetwork + */ + public Network createSubnetwork(int[] nodes) + { + double[] subnetworkEdgeWeights; + int i, j, k; + int[] subnetworkNodes, subnetworkNeighbors; + Network subnetwork; + + subnetwork = new Network(); + + subnetwork.nNodes = nodes.length; + + if (subnetwork.nNodes == 1) + { + subnetwork.nEdges = 0; + subnetwork.nodeWeights = new double[1]; + subnetwork.nodeWeights[0] = nodeWeights[nodes[0]]; + subnetwork.firstNeighborIndices = new int[2]; + subnetwork.neighbors = new int[0]; + subnetwork.edgeWeights = new double[0]; + } + else + { + subnetworkNodes = new int[nNodes]; + Arrays.fill(subnetworkNodes, -1); + for (i = 0; i < nodes.length; i++) + subnetworkNodes[nodes[i]] = i; + + subnetwork.nEdges = 0; + subnetwork.nodeWeights = new double[subnetwork.nNodes]; + subnetwork.firstNeighborIndices = new int[subnetwork.nNodes + 1]; + subnetworkNeighbors = new int[nEdges]; + subnetworkEdgeWeights = new double[nEdges]; + for (i = 0; i < subnetwork.nNodes; i++) + { + j = nodes[i]; + subnetwork.nodeWeights[i] = nodeWeights[j]; + for (k = firstNeighborIndices[j]; k < firstNeighborIndices[j + 1]; k++) + if (subnetworkNodes[neighbors[k]] >= 0) + { + subnetworkNeighbors[subnetwork.nEdges] = subnetworkNodes[neighbors[k]]; + subnetworkEdgeWeights[subnetwork.nEdges] = edgeWeights[k]; + subnetwork.nEdges++; + } + subnetwork.firstNeighborIndices[i + 1] = subnetwork.nEdges; + } + subnetwork.neighbors = Arrays.copyOfRange(subnetworkNeighbors, 0, subnetwork.nEdges); + subnetwork.edgeWeights = Arrays.copyOfRange(subnetworkEdgeWeights, 0, subnetwork.nEdges); + } + + subnetwork.totalEdgeWeightSelfLinks = 0; + + return subnetwork; + } + + /** + * Creates an induced subnetwork for specified nodes. + * + * @param nodesInSubnetwork Indicates the nodes to be included in the + * subnetwork. + * + * @return Subnetwork + */ + public Network createSubnetwork(boolean[] nodesInSubnetwork) + { + int i, j; + int[] nodes; + + i = 0; + for (j = 0; j < nNodes; j++) + if (nodesInSubnetwork[j]) + i++; + nodes = new int[i]; + i = 0; + for (j = 0; j < nNodes; j++) + if (nodesInSubnetwork[j]) + { + nodes[i] = j; + i++; + } + return createSubnetwork(nodes); + } + + /** + * Creates an induced subnetwork for a specified cluster in a clustering. + * + *

+ * If subnetworks need to be created for all clusters in a clustering, it + * is more efficient to use {@link #createSubnetworks(Clustering + * clustering)}. + *

+ * + * @param clustering Clustering + * @param cluster Cluster + * + * @return Subnetwork + */ + public Network createSubnetwork(Clustering clustering, int cluster) + { + double[] subnetworkEdgeWeights; + int[] subnetworkNeighbors, subnetworkNodes; + int[][] nodesPerCluster; + + nodesPerCluster = clustering.getNodesPerCluster(); + subnetworkNodes = new int[nNodes]; + subnetworkNeighbors = new int[nEdges]; + subnetworkEdgeWeights = new double[nEdges]; + return createSubnetwork(clustering, cluster, nodesPerCluster[cluster], subnetworkNodes, subnetworkNeighbors, subnetworkEdgeWeights); + } + + /** + * Creates induced subnetworks for the clusters in a clustering. + * + * @param clustering Clustering + * + * @return Subnetworks + */ + public Network[] createSubnetworks(Clustering clustering) + { + double[] subnetworkEdgeWeights; + int i; + int[] subnetworkNeighbors, subnetworkNodes; + int[][] nodesPerCluster; + Network[] subnetworks; + + subnetworks = new Network[clustering.nClusters]; + nodesPerCluster = clustering.getNodesPerCluster(); + subnetworkNodes = new int[nNodes]; + subnetworkNeighbors = new int[nEdges]; + subnetworkEdgeWeights = new double[nEdges]; + for (i = 0; i < clustering.nClusters; i++) + subnetworks[i] = createSubnetwork(clustering, i, nodesPerCluster[i], subnetworkNodes, subnetworkNeighbors, subnetworkEdgeWeights); + return subnetworks; + } + + /** + * Creates an induced subnetwork of the largest connected component. + * + * @return Subnetwork + */ + public Network createSubnetworkLargestComponent() + { + return createSubnetwork(identifyComponents(), 0); + } + + /** + * Creates a reduced (or aggregate) network based on a clustering. + * + *

+ * Each node in the reduced network corresponds to a cluster of nodes in + * the original network. The weight of a node in the reduced network equals + * the sum of the weights of the nodes in the corresponding cluster in the + * original network. The weight of an edge between two nodes in the reduced + * network equals the sum of the weights of the edges between the nodes in + * the two corresponding clusters in the original network. + *

+ * + * @param clustering Clustering + * + * @return Reduced network + */ + public Network createReducedNetwork(Clustering clustering) + { + double[] reducedNetworkEdgeWeights1, reducedNetworkEdgeWeights2; + int i, j, k, l, m, n; + int[] reducedNetworkNeighbors1, reducedNetworkNeighbors2; + int[][] nodesPerCluster; + Network reducedNetwork; + + reducedNetwork = new Network(); + + reducedNetwork.nNodes = clustering.nClusters; + + reducedNetwork.nEdges = 0; + reducedNetwork.nodeWeights = new double[clustering.nClusters]; + reducedNetwork.firstNeighborIndices = new int[clustering.nClusters + 1]; + reducedNetwork.totalEdgeWeightSelfLinks = totalEdgeWeightSelfLinks; + reducedNetworkNeighbors1 = new int[nEdges]; + reducedNetworkEdgeWeights1 = new double[nEdges]; + reducedNetworkNeighbors2 = new int[clustering.nClusters - 1]; + reducedNetworkEdgeWeights2 = new double[clustering.nClusters]; + nodesPerCluster = clustering.getNodesPerCluster(); + for (i = 0; i < clustering.nClusters; i++) + { + j = 0; + for (k = 0; k < nodesPerCluster[i].length; k++) + { + l = nodesPerCluster[i][k]; + + reducedNetwork.nodeWeights[i] += nodeWeights[l]; + + for (m = firstNeighborIndices[l]; m < firstNeighborIndices[l + 1]; m++) + { + n = clustering.clusters[neighbors[m]]; + if (n != i) + { + if (reducedNetworkEdgeWeights2[n] == 0) + { + reducedNetworkNeighbors2[j] = n; + j++; + } + reducedNetworkEdgeWeights2[n] += edgeWeights[m]; + } + else + reducedNetwork.totalEdgeWeightSelfLinks += edgeWeights[m]; + } + } + + for (k = 0; k < j; k++) + { + reducedNetworkNeighbors1[reducedNetwork.nEdges + k] = reducedNetworkNeighbors2[k]; + reducedNetworkEdgeWeights1[reducedNetwork.nEdges + k] = reducedNetworkEdgeWeights2[reducedNetworkNeighbors2[k]]; + reducedNetworkEdgeWeights2[reducedNetworkNeighbors2[k]] = 0; + } + reducedNetwork.nEdges += j; + reducedNetwork.firstNeighborIndices[i + 1] = reducedNetwork.nEdges; + } + reducedNetwork.neighbors = Arrays.copyOfRange(reducedNetworkNeighbors1, 0, reducedNetwork.nEdges); + reducedNetwork.edgeWeights = Arrays.copyOfRange(reducedNetworkEdgeWeights1, 0, reducedNetwork.nEdges); + + return reducedNetwork; + } + + /** + * Identifies the connected components of the network. + * + * @return Connected components + */ + public Clustering identifyComponents() + { + ComponentsAlgorithm componentsAlgorithm; + + componentsAlgorithm = new ComponentsAlgorithm(); + return componentsAlgorithm.findClustering(this); + } + + /** + * Checks the integrity of the network. + * + *

+ * It is checked whether: + *

+ * + *
    + *
  • variables have a correct value,
  • + *
  • arrays have a correct length,
  • + *
  • edges are sorted correctly,
  • + *
  • edges are stored in both directions.
  • + *
+ * + *

+ * An exception is thrown if the integrity of the network is violated. + *

+ * + * @throws IllegalArgumentException An illegal argument was provided in the + * construction of the network. + */ + public void checkIntegrity() throws IllegalArgumentException + { + boolean[] checked; + int i, j, k, l; + + // Check whether variables have a correct value and arrays have a + // correct length. + if (nNodes < 0) + throw new IllegalArgumentException("nNodes must be non-negative."); + + if (nEdges < 0) + throw new IllegalArgumentException("nEdges must be non-negative."); + + if (nEdges % 2 == 1) + throw new IllegalArgumentException("nEdges must be even."); + + if (nodeWeights.length != nNodes) + throw new IllegalArgumentException("Length of nodeWeight array must be equal to nNodes."); + + if (firstNeighborIndices.length != nNodes + 1) + throw new IllegalArgumentException("Length of firstNeighborIndices array must be equal to nNodes + 1."); + + if (firstNeighborIndices[0] != 0) + throw new IllegalArgumentException("First element of firstNeighborIndices array must be equal to 0."); + + if (firstNeighborIndices[nNodes] != nEdges) + throw new IllegalArgumentException("Last element of firstNeighborIndices array must be equal to nEdges."); + + if (neighbors.length != nEdges) + throw new IllegalArgumentException("Length of neighbors array must be equal to nEdges."); + + if (edgeWeights.length != nEdges) + throw new IllegalArgumentException("Length of edgeWeights array must be equal to nEdges."); + + // Check whether edges are sorted correctly. + for (i = 0; i < nNodes; i++) + { + if (firstNeighborIndices[i + 1] < firstNeighborIndices[i]) + throw new IllegalArgumentException("Elements of firstNeighborIndices array must be in non-decreasing order."); + + for (j = firstNeighborIndices[i]; j < firstNeighborIndices[i + 1]; j++) + { + k = neighbors[j]; + + if (k < 0) + throw new IllegalArgumentException("Elements of neighbors array must have non-negative values."); + else if (k >= nNodes) + throw new IllegalArgumentException("Elements of neighbors array must have values less than nNodes."); + + if (j > firstNeighborIndices[i]) + { + l = neighbors[j - 1]; + if (k < l) + throw new IllegalArgumentException("For each node, corresponding elements of neighbors array must be in increasing order."); + else if (k == l) + throw new IllegalArgumentException("For each node, corresponding elements of neighbors array must not include duplicate values."); + } + } + } + + // Check whether edges are stored in both directions. + checked = new boolean[nEdges]; + for (i = 0; i < nNodes; i++) + for (j = firstNeighborIndices[i]; j < firstNeighborIndices[i + 1]; j++) + if (!checked[j]) + { + k = neighbors[j]; + + l = Arrays.binarySearch(neighbors, firstNeighborIndices[k], firstNeighborIndices[k + 1], i); + if (l < 0) + throw new IllegalArgumentException("Edges must be stored in both directions."); + if (edgeWeights[j] != edgeWeights[l]) + throw new IllegalArgumentException("Edge weights must be the same in both directions."); + + checked[j] = true; + checked[l] = true; + } + } + + private static void sortEdges(int[][] edges, double[] edgeWeights) + { + class EdgeComparator implements Comparator + { + int[][] edges; + + public EdgeComparator(int[][] edges) + { + this.edges = edges; + } + + public int compare(Integer i, Integer j) + { + if (edges[0][i] > edges[0][j]) + return 1; + if (edges[0][i] < edges[0][j]) + return -1; + if (edges[1][i] > edges[1][j]) + return 1; + if (edges[1][i] < edges[1][j]) + return -1; + return 0; + } + } + + double[] edgeWeightsSorted; + int i, nEdges; + int[][] edgesSorted; + Integer[] indices; + + nEdges = edges[0].length; + + // Determine sorting order. + indices = new Integer[nEdges]; + for (i = 0; i < nEdges; i++) + indices[i] = i; + Arrays.parallelSort(indices, new EdgeComparator(edges)); + + // Sort edges. + edgesSorted = new int[2][nEdges]; + for (i = 0; i < nEdges; i++) + { + edgesSorted[0][i] = edges[0][indices[i]]; + edgesSorted[1][i] = edges[1][indices[i]]; + } + edges[0] = edgesSorted[0]; + edges[1] = edgesSorted[1]; + + // Sort edge weights. + if (edgeWeights != null) + { + edgeWeightsSorted = new double[nEdges]; + for (i = 0; i < nEdges; i++) + edgeWeightsSorted[i] = edgeWeights[indices[i]]; + System.arraycopy(edgeWeightsSorted, 0, edgeWeights, 0, nEdges); + } + } + + private Network() + { + } + + private Network(int nNodes, double[] nodeWeights, boolean setNodeWeightsToTotalEdgeWeights, int[][] edges, double[] edgeWeights, boolean sortedEdges, boolean checkIntegrity) + { + double[] edgeWeights2; + int i, j; + int[][] edges2; + + if (!sortedEdges) + { + edges2 = new int[2][2 * edges[0].length]; + edgeWeights2 = (edgeWeights != null) ? new double[2 * edges[0].length] : null; + i = 0; + for (j = 0; j < edges[0].length; j++) + { + edges2[0][i] = edges[0][j]; + edges2[1][i] = edges[1][j]; + if (edgeWeights != null) + edgeWeights2[i] = edgeWeights[j]; + i++; + if (edges[0][j] != edges[1][j]) + { + edges2[0][i] = edges[1][j]; + edges2[1][i] = edges[0][j]; + if (edgeWeights != null) + edgeWeights2[i] = edgeWeights[j]; + i++; + } + } + edges[0] = Arrays.copyOfRange(edges2[0], 0, i); + edges[1] = Arrays.copyOfRange(edges2[1], 0, i); + if (edgeWeights != null) + edgeWeights = Arrays.copyOfRange(edgeWeights2, 0, i); + sortEdges(edges, edgeWeights); + } + + this.nNodes = nNodes; + nEdges = 0; + firstNeighborIndices = new int[nNodes + 1]; + neighbors = new int[edges[0].length]; + this.edgeWeights = new double[edges[0].length]; + totalEdgeWeightSelfLinks = 0; + i = 1; + for (j = 0; j < edges[0].length; j++) + if (edges[0][j] != edges[1][j]) + { + for (; i <= edges[0][j]; i++) + firstNeighborIndices[i] = nEdges; + neighbors[nEdges] = edges[1][j]; + this.edgeWeights[nEdges] = (edgeWeights != null) ? edgeWeights[j] : 1; + nEdges++; + } + else + totalEdgeWeightSelfLinks += (edgeWeights != null) ? edgeWeights[j] : 1; + for (; i <= nNodes; i++) + firstNeighborIndices[i] = nEdges; + neighbors = Arrays.copyOfRange(neighbors, 0, nEdges); + this.edgeWeights = Arrays.copyOfRange(this.edgeWeights, 0, nEdges); + + this.nodeWeights = (nodeWeights != null) ? nodeWeights.clone() : (setNodeWeightsToTotalEdgeWeights ? getTotalEdgeWeightPerNodeHelper() : cwts.util.Arrays.createDoubleArrayOfOnes(nNodes)); + + if (checkIntegrity) + checkIntegrity(); + } + + private Network(int nNodes, double[] nodeWeights, boolean setNodeWeightsToTotalEdgeWeights, int[] firstNeighborIndices, int[] neighbors, double[] edgeWeights, boolean checkIntegrity) + { + this.nNodes = nNodes; + nEdges = neighbors.length; + this.firstNeighborIndices = firstNeighborIndices.clone(); + this.neighbors = neighbors.clone(); + this.edgeWeights = (edgeWeights != null) ? edgeWeights.clone() : cwts.util.Arrays.createDoubleArrayOfOnes(nEdges); + totalEdgeWeightSelfLinks = 0; + + this.nodeWeights = (nodeWeights != null) ? nodeWeights.clone() : (setNodeWeightsToTotalEdgeWeights ? getTotalEdgeWeightPerNodeHelper() : cwts.util.Arrays.createDoubleArrayOfOnes(nNodes)); + + if (checkIntegrity) + checkIntegrity(); + } + + private double[] getTotalEdgeWeightPerNodeHelper() + { + double[] totalEdgeWeightPerNode; + int i; + + totalEdgeWeightPerNode = new double[nNodes]; + for (i = 0; i < nNodes; i++) + totalEdgeWeightPerNode[i] = cwts.util.Arrays.calcSum(edgeWeights, firstNeighborIndices[i], firstNeighborIndices[i + 1]); + return totalEdgeWeightPerNode; + } + + private double getRandomNumber(int node1, int node2, double[] randomNumbers) + { + int i, j; + + if (node1 < node2) + { + i = node1; + j = node2; + } + else + { + i = node2; + j = node1; + } + return randomNumbers[i * nNodes + j]; + } + + private Network createSubnetwork(Clustering clustering, int cluster, int[] nodes, int[] subnetworkNodes, int[] subnetworkNeighbors, double[] subnetworkEdgeWeights) + { + int i, j, k; + Network subnetwork; + + subnetwork = new Network(); + + subnetwork.nNodes = nodes.length; + + if (subnetwork.nNodes == 1) + { + subnetwork.nEdges = 0; + subnetwork.nodeWeights = new double[1]; + subnetwork.nodeWeights[0] = nodeWeights[nodes[0]]; + subnetwork.firstNeighborIndices = new int[2]; + subnetwork.neighbors = new int[0]; + subnetwork.edgeWeights = new double[0]; + } + else + { + for (i = 0; i < nodes.length; i++) + subnetworkNodes[nodes[i]] = i; + + subnetwork.nEdges = 0; + subnetwork.nodeWeights = new double[subnetwork.nNodes]; + subnetwork.firstNeighborIndices = new int[subnetwork.nNodes + 1]; + for (i = 0; i < subnetwork.nNodes; i++) + { + j = nodes[i]; + subnetwork.nodeWeights[i] = nodeWeights[j]; + for (k = firstNeighborIndices[j]; k < firstNeighborIndices[j + 1]; k++) + if (clustering.clusters[neighbors[k]] == cluster) + { + subnetworkNeighbors[subnetwork.nEdges] = subnetworkNodes[neighbors[k]]; + subnetworkEdgeWeights[subnetwork.nEdges] = edgeWeights[k]; + subnetwork.nEdges++; + } + subnetwork.firstNeighborIndices[i + 1] = subnetwork.nEdges; + } + subnetwork.neighbors = Arrays.copyOfRange(subnetworkNeighbors, 0, subnetwork.nEdges); + subnetwork.edgeWeights = Arrays.copyOfRange(subnetworkEdgeWeights, 0, subnetwork.nEdges); + } + + subnetwork.totalEdgeWeightSelfLinks = 0; + + return subnetwork; + } +} diff --git a/src/cwts/networkanalysis/QualityClusteringAlgorithm.java b/src/cwts/networkanalysis/QualityClusteringAlgorithm.java new file mode 100644 index 0000000..ea30679 --- /dev/null +++ b/src/cwts/networkanalysis/QualityClusteringAlgorithm.java @@ -0,0 +1,21 @@ +package cwts.networkanalysis; + +/** + * Interface for clustering algorithms that use a quality function. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public interface QualityClusteringAlgorithm extends ClusteringAlgorithm +{ + /** + * Calculates the quality of a clustering of the nodes in a network. + * + * @param network Network + * @param clustering Clustering + * + * @return Quality of the clustering + */ + public double calcQuality(Network network, Clustering clustering); +} diff --git a/src/cwts/networkanalysis/StandardLocalMovingAlgorithm.java b/src/cwts/networkanalysis/StandardLocalMovingAlgorithm.java new file mode 100644 index 0000000..ff3d95e --- /dev/null +++ b/src/cwts/networkanalysis/StandardLocalMovingAlgorithm.java @@ -0,0 +1,221 @@ +package cwts.networkanalysis; + +import cwts.util.Arrays; +import java.util.Random; + +/** + * Standard local moving algorithm. + * + *

+ * The standard local moving algorithm iterates over the nodes in a network. A + * node is moved to the cluster that results in the largest increase in the + * quality function. If the current cluster assignment of the node is already + * optimal, the node is not moved. The algorithm continues iterating over the + * nodes in a network until no more nodes can be moved. + *

+ * + *

+ * A fast variant of the standard local moving algorithm is provided by the + * {@link FastLocalMovingAlgorithm}. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class StandardLocalMovingAlgorithm extends IncrementalCPMClusteringAlgorithm +{ + /** + * Random number generator. + */ + protected Random random; + + /** + * Constructs a standard local moving algorithm. + */ + public StandardLocalMovingAlgorithm() + { + this(new Random()); + } + + /** + * Constructs a standard local moving algorithm. + * + * @param random Random number generator + */ + public StandardLocalMovingAlgorithm(Random random) + { + this(DEFAULT_RESOLUTION, random); + } + + /** + * Constructs a standard local moving algorithm for a specified resolution + * parameter. + * + * @param resolution Resolution parameter + * @param random Random number generator + */ + public StandardLocalMovingAlgorithm(double resolution, Random random) + { + super(resolution); + + this.random = random; + } + + /** + * Improves a clustering of the nodes in a network using the standard local + * moving algorithm. + * + *

+ * The standard local moving algorithm iterates over the nodes in a + * network. A node is moved to the cluster that results in the largest + * increase in the quality function. If the current cluster assignment of + * the node is already optimal, the node is not moved. The algorithm + * continues iterating over the nodes in a network until no more nodes can + * be moved. + *

+ * + * @param network Network + * @param clustering Clustering + * + * @return Boolean indicating whether the clustering has been improved + */ + public boolean improveClustering(Network network, Clustering clustering) + { + boolean update; + double maxQualityValueIncrement, qualityValueIncrement; + double[] clusterWeights, edgeWeightPerCluster; + int bestCluster, currentCluster, i, j, k, l, nNeighboringClusters, nUnstableNodes, nUnusedClusters; + int[] neighboringClusters, nNodesPerCluster, nodeOrder, unusedClusters; + + if (network.nNodes == 1) + return false; + + update = false; + + clusterWeights = new double[network.nNodes]; + nNodesPerCluster = new int[network.nNodes]; + for (i = 0; i < network.nNodes; i++) + { + clusterWeights[clustering.clusters[i]] += network.nodeWeights[i]; + nNodesPerCluster[clustering.clusters[i]]++; + } + + nUnusedClusters = 0; + unusedClusters = new int[network.nNodes - 1]; + for (i = network.nNodes - 1; i >= 0; i--) + if (nNodesPerCluster[i] == 0) + { + unusedClusters[nUnusedClusters] = i; + nUnusedClusters++; + } + + nodeOrder = Arrays.generateRandomPermutation(network.nNodes, random); + + /* + * Iterate over the nodeOrder array in a cyclical manner. When the end + * of the array has been reached, start again from the beginning. + * Continue iterating until none of the last nNodes node visits has + * resulted in a node movement. + */ + edgeWeightPerCluster = new double[network.nNodes]; + neighboringClusters = new int[network.nNodes]; + nUnstableNodes = network.nNodes; + i = 0; + do + { + j = nodeOrder[i]; + + currentCluster = clustering.clusters[j]; + + // Remove the currently selected node from its current cluster. + clusterWeights[currentCluster] -= network.nodeWeights[j]; + nNodesPerCluster[currentCluster]--; + if (nNodesPerCluster[currentCluster] == 0) + { + unusedClusters[nUnusedClusters] = currentCluster; + nUnusedClusters++; + } + + /* + * Identify the neighboring clusters of the currently selected + * node, that is, the clusters with which the currently selected + * node is connected. An empty cluster is also included in the set + * of neighboring clusters. In this way, it is always possible that + * the currently selected node will be moved to an empty cluster. + */ + neighboringClusters[0] = unusedClusters[nUnusedClusters - 1]; + nNeighboringClusters = 1; + for (k = network.firstNeighborIndices[j]; k < network.firstNeighborIndices[j + 1]; k++) + { + l = clustering.clusters[network.neighbors[k]]; + if (edgeWeightPerCluster[l] == 0) + { + neighboringClusters[nNeighboringClusters] = l; + nNeighboringClusters++; + } + edgeWeightPerCluster[l] += network.edgeWeights[k]; + } + + /* + * For each neighboring cluster of the currently selected node, + * calculate the increment of the quality function obtained by + * moving the currently selected node to the neighboring cluster. + * Determine the neighboring cluster for which the increment of the + * quality function is largest. The currently selected node will be + * moved to this optimal cluster. In order to guarantee convergence + * of the algorithm, if the old cluster of the currently selected + * node is optimal but there are also other optimal clusters, the + * currently selected node will be moved back to its old cluster. + */ + bestCluster = currentCluster; + maxQualityValueIncrement = edgeWeightPerCluster[currentCluster] - network.nodeWeights[j] * clusterWeights[currentCluster] * resolution; + for (k = 0; k < nNeighboringClusters; k++) + { + l = neighboringClusters[k]; + + qualityValueIncrement = edgeWeightPerCluster[l] - network.nodeWeights[j] * clusterWeights[l] * resolution; + if (qualityValueIncrement > maxQualityValueIncrement) + { + bestCluster = l; + maxQualityValueIncrement = qualityValueIncrement; + } + + edgeWeightPerCluster[l] = 0; + } + + /* + * Move the currently selected node to its new cluster. Update the + * clustering statistics. + */ + clusterWeights[bestCluster] += network.nodeWeights[j]; + nNodesPerCluster[bestCluster]++; + if (bestCluster == unusedClusters[nUnusedClusters - 1]) + nUnusedClusters--; + nUnstableNodes--; + + /* + * If the new cluster of the currently selected node is different + * from the old cluster, some further updating of the clustering + * statistics is performed. + */ + if (bestCluster != currentCluster) + { + clustering.clusters[j] = bestCluster; + if (bestCluster >= clustering.nClusters) + clustering.nClusters = bestCluster + 1; + + nUnstableNodes = network.nNodes; + + update = true; + } + + i = (i < network.nNodes - 1) ? (i + 1) : 0; + } while (nUnstableNodes > 0); + + if (update) + clustering.removeEmptyClusters(); + + return update; + } +} diff --git a/src/cwts/networkanalysis/package-info.java b/src/cwts/networkanalysis/package-info.java new file mode 100644 index 0000000..45c4037 --- /dev/null +++ b/src/cwts/networkanalysis/package-info.java @@ -0,0 +1,23 @@ +/** + * Provides data structures and algorithms for network analysis. + * + *

+ * Currently, the focus of this package is restricted to clustering algorithms + * (also known as community detection algorithms) and the associated data + * structures. + *

+ * + *

+ * The classes {@link cwts.networkanalysis.Network} and {@link + * cwts.networkanalysis.Clustering} represent the core data structures. The + * classes {@link cwts.networkanalysis.LeidenAlgorithm} and {@link + * cwts.networkanalysis.LouvainAlgorithm} represent the core clustering + * algorithms. These two classes are embedded in a hierarchy of classes and + * interfaces for representing clustering algorithms. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +package cwts.networkanalysis; diff --git a/src/cwts/networkanalysis/run/RunNetworkClustering.java b/src/cwts/networkanalysis/run/RunNetworkClustering.java new file mode 100644 index 0000000..42bdb00 --- /dev/null +++ b/src/cwts/networkanalysis/run/RunNetworkClustering.java @@ -0,0 +1,599 @@ +package cwts.networkanalysis.run; + +import cwts.networkanalysis.Clustering; +import cwts.networkanalysis.CPMClusteringAlgorithm; +import cwts.networkanalysis.IterativeCPMClusteringAlgorithm; +import cwts.networkanalysis.LeidenAlgorithm; +import cwts.networkanalysis.LouvainAlgorithm; +import cwts.networkanalysis.Network; +import cwts.util.DynamicDoubleArray; +import cwts.util.DynamicIntArray; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.Random; + +/** + * Command line tool for running the Leiden and Louvain algorithms for network + * clustering. + * + *

+ * All methods in this class are static. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public final class RunNetworkClustering +{ + /** + * Default quality function. + */ + public static final boolean DEFAULT_USE_MODULARITY = false; + + /** + * Default resolution parameter. + */ + public static final double DEFAULT_RESOLUTION = CPMClusteringAlgorithm.DEFAULT_RESOLUTION; + + /** + * Default clustering algorithm. + */ + public static final boolean DEFAULT_USE_LOUVAIN = false; + + /** + * Default number of random starts. + */ + public static final int DEFAULT_N_RANDOM_STARTS = 1; + + /** + * Default number of iterations. + */ + public static final int DEFAULT_N_ITERATIONS = 10; + + /** + * Default randomness parameter. + */ + public static final double DEFAULT_RANDOMNESS = LeidenAlgorithm.DEFAULT_RANDOMNESS; + + /** + * Description text. + */ + public static final String DESCRIPTION + = "RunNetworkClustering version 1.0.0\n" + + "By Vincent Traag, Ludo Waltman, and Nees Jan van Eck\n" + + "Centre for Science and Technology Studies (CWTS), Leiden University\n"; + + /** + * Usage text. + */ + public static final String USAGE + = "Usage: RunNetworkClustering [options] \n" + + "\n" + + "Identify clusters (also known as communities) in a network, using either the\n" + + "Leiden or the Louvain algorithm.\n" + + "\n" + + "The file in is expected to contain a tab-separated edge list\n" + + "(without a header line). Nodes are represented by zero-index integer numbers.\n" + + "Only undirected networks are supported. Each edge should be included only once\n" + + "in the file.\n" + + "\n" + + "Options:\n" + + "-q --quality-function {CPM|modularity} (default: CPM)\n" + + " Quality function to be optimized. Either the CPM (constant Potts model) or\n" + + " the modularity quality function can be used.\n" + + "-r --resolution (default: " + DEFAULT_RESOLUTION + ")\n" + + " Resolution parameter of the quality function.\n" + + "-a --algorithm {Leiden|Louvain} (default: Leiden)\n" + + " Algorithm for optimizing the quality function. Either the Leiden or the\n" + + " Louvain algorithm can be used.\n" + + "-s --random-starts (default: " + DEFAULT_N_RANDOM_STARTS + ")\n" + + " Number of random starts of the algorithm.\n" + + "-i --iterations (default: " + DEFAULT_N_ITERATIONS + ")\n" + + " Number of iterations of the algorithm.\n" + + "--randomness (default: " + DEFAULT_RANDOMNESS + ")\n" + + " Randomness parameter of the Leiden algorithm.\n" + + "--seed (default: random)\n" + + " Seed of the random number generator.\n" + + "-w --weighted-edges\n" + + " Indicates that the edge list file has a third column containing edge\n" + + " weights.\n" + + "--sorted-edge-list\n" + + " Indicates that the edge list file is sorted. The file should be sorted based\n" + + " on the nodes in the first column, followed by the nodes in the second\n" + + " column. Each edge should be included in both directions in the file.\n" + + "--input-clustering (default: singleton clustering)\n" + + " Read the initial clustering from the specified file. The file is expected to\n" + + " contain two tab-separated columns (without a header line), first a column of\n" + + " nodes and then a column of clusters. Nodes and clusters are both represented\n" + + " by zero-index integer numbers. If no file is specified, a singleton\n" + + " clustering (in which each node has its own cluster) is used as the initial\n" + + " clustering.\n" + + "-o --output-clustering (default: standard output)\n" + + " Write the final clustering to the specified file. If no file is specified,\n" + + " the standard output is used.\n"; + + /** + * Column separator for edge list and clustering files. + */ + public static final String COLUMN_SEPARATOR = "\t"; + + /** + * This method is called when the tool is started. + * + * @param args Command line arguments + */ + public static void main(String[] args) + { + System.err.println(DESCRIPTION); + + // Process command line arguments. + if (args.length == 0) + { + System.err.print(USAGE); + System.exit(-1); + } + + boolean useModularity = DEFAULT_USE_MODULARITY; + double resolution = DEFAULT_RESOLUTION; + boolean useLouvain = DEFAULT_USE_LOUVAIN; + int nRandomStarts = DEFAULT_N_RANDOM_STARTS; + int nIterations = DEFAULT_N_ITERATIONS; + double randomness = DEFAULT_RANDOMNESS; + + long seed = 0; + boolean useSeed = false; + boolean weightedEdges = false; + boolean sortedEdgeList = false; + String initialClusteringFilename = null; + String finalClusteringFilename = null; + String edgeListFilename = null; + + int argIndex = 0; + while (argIndex < args.length - 1) + { + String arg = args[argIndex]; + try + { + if (arg.equals("-q") || arg.equals("--quality-function")) + { + if (((argIndex + 1) >= args.length) || (!args[argIndex + 1].equals("CPM") && !args[argIndex + 1].equals("modularity"))) + throw new IllegalArgumentException("Value must be 'CPM' or 'modularity'."); + useModularity = args[argIndex + 1].equals("modularity"); + argIndex += 2; + } + else if (arg.equals("-r") || arg.equals("--resolution")) + { + try + { + if ((argIndex + 1) >= args.length) + throw new NumberFormatException(); + resolution = Double.parseDouble(args[argIndex + 1]); + if (resolution < 0) + throw new NumberFormatException(); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException("Value must be a non-negative number."); + } + argIndex += 2; + } + else if (arg.equals("-a") || arg.equals("--algorithm")) + { + if (((argIndex + 1) >= args.length) || (!args[argIndex + 1].equals("Leiden") && !args[argIndex + 1].equals("Louvain"))) + throw new IllegalArgumentException("Value must be 'Leiden' or 'Louvain'."); + useLouvain = args[argIndex + 1].equals("Louvain"); + argIndex += 2; + } + else if (arg.equals("-s") || arg.equals("--random-starts")) + { + try + { + if ((argIndex + 1) >= args.length) + throw new NumberFormatException(); + nRandomStarts = Integer.parseInt(args[argIndex + 1]); + if (nRandomStarts <= 0) + throw new NumberFormatException(); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException("Value must be a positive integer number."); + } + argIndex += 2; + } + else if (arg.equals("-i") || arg.equals("--iterations")) + { + try + { + if ((argIndex + 1) >= args.length) + throw new NumberFormatException(); + nIterations = Integer.parseInt(args[argIndex + 1]); + if (nIterations <= 0) + throw new NumberFormatException(); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException("Value must be a positive integer number."); + } + argIndex += 2; + } + else if (arg.equals("--randomness")) + { + try + { + if ((argIndex + 1) >= args.length) + throw new NumberFormatException(); + randomness = Double.parseDouble(args[argIndex + 1]); + if (randomness <= 0) + throw new NumberFormatException(); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException("Value must be a positive number."); + } + argIndex += 2; + } + else if (arg.equals("--seed")) + { + try + { + if ((argIndex + 1) >= args.length) + throw new NumberFormatException(); + seed = Long.parseLong(args[argIndex + 1]); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException("Value must be an integer number."); + } + useSeed = true; + argIndex += 2; + } + else if (arg.equals("-w") || arg.equals("--weighted-edges")) + { + weightedEdges = true; + argIndex++; + } + else if (arg.equals("--sorted-edge-list")) + { + sortedEdgeList = true; + argIndex++; + } + else if (arg.equals("--input-clustering")) + { + if ((argIndex + 1) >= args.length) + throw new IllegalArgumentException("Missing value."); + initialClusteringFilename = args[argIndex + 1]; + argIndex += 2; + } + else if (arg.equals("-o") || arg.equals("--output-clustering")) + { + if ((argIndex + 1) >= args.length) + throw new IllegalArgumentException("Missing value."); + finalClusteringFilename = args[argIndex + 1]; + argIndex += 2; + } + else + throw new IllegalArgumentException("Invalid command line argument."); + } + catch (IllegalArgumentException e) + { + System.err.print("Error while processing command line argument " + arg + ": " + e.getMessage() + "\n\n" + USAGE); + System.exit(-1); + } + } + if (argIndex >= args.length) + { + System.err.print("Error while processing command line arguments: Incorrect number of command line arguments.\n\n" + USAGE); + System.exit(-1); + } + edgeListFilename = args[argIndex]; + + // Read edge list from file. + System.err.println("Reading " + (sortedEdgeList ? "sorted " : "") + "edge list from '" + edgeListFilename + "'."); + long startTimeEdgeListFile = System.currentTimeMillis(); + Network network = readEdgeList(edgeListFilename, useModularity, weightedEdges, sortedEdgeList); + System.err.println("Reading " + (sortedEdgeList ? "sorted " : "") + "edge list took " + (System.currentTimeMillis() - startTimeEdgeListFile) / 1000 + "s."); + System.err.println("Network consists of " + network.getNNodes() + " nodes and " + network.getNEdges() + " edges" + (weightedEdges ? " with a total edge weight of " + network.getTotalEdgeWeight() : "") + "."); + + // Read initial clustering from file. + Clustering initialClustering = null; + if (initialClusteringFilename == null) + { + System.err.println("Using singleton initial clustering."); + initialClustering = new Clustering(network.getNNodes()); + } + else + { + System.err.println("Reading initial clustering from '" + initialClusteringFilename + "'."); + initialClustering = readClustering(initialClusteringFilename, network.getNNodes()); + System.err.println("Initial clustering consists of " + initialClustering.getNClusters() + " clusters."); + } + + // Run algorithm for network clustering. + System.err.println("Running " + (useLouvain ? "Louvain" : "Leiden") + " algorithm."); + System.err.println("Quality function: " + (useModularity ? "modularity" : "CPM")); + System.err.println("Resolution parameter: " + resolution); + if ((!weightedEdges) && (!useModularity) && (resolution >= 1)) + System.err.println("Warning: When applying the CPM quality function in an unweighted network, the resolution parameter should have a value below 1."); + System.err.println("Number of random starts: " + nRandomStarts); + System.err.println("Number of iterations: " + nIterations); + if (!useLouvain) + System.err.println("Randomness parameter: " + randomness); + System.err.println("Random number generator seed: " + (useSeed ? seed : "random")); + + long startTimeAlgorithm = System.currentTimeMillis(); + double resolution2 = useModularity ? (resolution / (2 * network.getTotalEdgeWeight() + network.getTotalEdgeWeightSelfLinks())) : resolution; + Random random = useSeed ? new Random(seed) : new Random(); + IterativeCPMClusteringAlgorithm algorithm = useLouvain ? new LouvainAlgorithm(resolution2, nIterations, random) : new LeidenAlgorithm(resolution2, nIterations, randomness, random); + Clustering finalClustering = null; + double maxQuality = Double.NEGATIVE_INFINITY; + for (int i = 0; i < nRandomStarts; i++) + { + Clustering clustering = initialClustering.clone(); + algorithm.improveClustering(network, clustering); + double quality = algorithm.calcQuality(network, clustering); + if (nRandomStarts > 1) + System.err.println("Quality function in random start " + (i + 1) + " equals " + quality + "."); + if (quality > maxQuality) + { + finalClustering = clustering; + maxQuality = quality; + } + } + System.err.println("Running algorithm took " + (System.currentTimeMillis() - startTimeAlgorithm) / 1000 + "s."); + if (nRandomStarts > 1) + System.err.println("Maximum value of quality function in " + nRandomStarts + " random starts equals " + maxQuality + "."); + else + System.err.println("Quality function equals " + maxQuality + "."); + System.err.println("Final clustering consists of " + finalClustering.getNClusters() + " clusters."); + + // Write final clustering to file (or to standard output). + System.err.println("Writing final clustering to " + ((finalClusteringFilename == null) ? "standard output." : "'" + finalClusteringFilename + "'.")); + writeClustering(finalClusteringFilename, finalClustering); + } + + /** + * Reads an edge list from a file and creates a network. + * + * @param filename Filename + * @param useModularity Indicates whether to use the modularity or the CPM + * quality function + * @param weightedEdges Indicates whether edges have weights + * @param sortedEdgeList Indicates whether the edge list is sorted + * + * @return Network + */ + public static Network readEdgeList(String filename, boolean useModularity, boolean weightedEdges, boolean sortedEdgeList) + { + // Read edge list. + DynamicIntArray[] edges = new DynamicIntArray[2]; + edges[0] = new DynamicIntArray(100); + edges[1] = new DynamicIntArray(100); + DynamicDoubleArray edgeWeights = weightedEdges ? new DynamicDoubleArray(100) : null; + int nNodes = 0; + BufferedReader reader = null; + try + { + reader = new BufferedReader(new FileReader(filename)); + String line = reader.readLine(); + int lineNo = 0; + while (line != null) + { + lineNo++; + String[] columns = line.split(COLUMN_SEPARATOR); + if ((!weightedEdges && ((columns.length < 2) || (columns.length > 3))) || (weightedEdges && (columns.length != 3))) + throw new IOException("Incorrect number of columns (line " + lineNo + ")."); + + int node1; + int node2; + try + { + node1 = Integer.parseUnsignedInt(columns[0]); + node2 = Integer.parseUnsignedInt(columns[1]); + } + catch (NumberFormatException e) + { + throw new IOException("Node must be represented by a zero-index integer number (line " + lineNo + ")."); + } + edges[0].append(node1); + edges[1].append(node2); + if (node1 >= nNodes) + nNodes = node1 + 1; + if (node2 >= nNodes) + nNodes = node2 + 1; + + if (weightedEdges) + { + double weight; + try + { + weight = Double.parseDouble(columns[2]); + } + catch (NumberFormatException e) + { + throw new IOException("Edge weight must be a number (line " + lineNo + ")."); + } + edgeWeights.append(weight); + } + + line = reader.readLine(); + } + } + catch (FileNotFoundException e) + { + System.err.println("Error while reading edge list from file: File not found."); + System.exit(-1); + } + catch (IOException e) + { + System.err.println("Error while reading edge list from file: " + e.getMessage()); + System.exit(-1); + } + finally + { + if (reader != null) + try + { + reader.close(); + } + catch (IOException e) + { + System.err.println("Error while reading edge list from file: " + e.getMessage()); + System.exit(-1); + } + } + + // Create network. + Network network = null; + int[][] edges2 = new int[2][]; + edges2[0] = edges[0].toArray(); + edges2[1] = edges[1].toArray(); + try + { + if (weightedEdges) + network = new Network(nNodes, useModularity, edges2, edgeWeights.toArray(), sortedEdgeList, true); + else + network = new Network(nNodes, useModularity, edges2, sortedEdgeList, true); + } + catch (IllegalArgumentException e) + { + System.err.println("Error while creating network: " + e.getMessage()); + System.exit(-1); + } + return network; + } + + /** + * Reads a clustering from a file. + * + * @param filename Filename + * @param nNodes Number of nodes + * + * @return Clustering + */ + public static Clustering readClustering(String filename, int nNodes) + { + int[] clusters = new int[nNodes]; + Arrays.fill(clusters, -1); + BufferedReader reader = null; + try + { + reader = new BufferedReader(new FileReader(filename)); + String line = reader.readLine(); + int lineNo = 0; + while (line != null) + { + lineNo++; + String[] columns = line.split(COLUMN_SEPARATOR); + if (columns.length != 2) + throw new IOException("Incorrect number of columns (line " + lineNo + ")."); + + int node; + try + { + node = Integer.parseUnsignedInt(columns[0]); + } + catch (NumberFormatException e) + { + throw new IOException("Node must be represented by a zero-index integer number (line " + lineNo + ")."); + } + if (node >= nNodes) + throw new IOException("Invalid node (line " + lineNo + ")."); + int cluster; + try + { + cluster = Integer.parseUnsignedInt(columns[1]); + } + catch (NumberFormatException e) + { + throw new IOException("Cluster must be represented by a zero-index integer number (line " + lineNo + ")."); + } + if (clusters[node] >= 0) + throw new IOException("Duplicate node (line " + lineNo + ")."); + clusters[node] = cluster; + + line = reader.readLine(); + } + if (lineNo < nNodes) + throw new IOException("Missing nodes."); + } + catch (FileNotFoundException e) + { + System.err.println("Error while reading clustering from file: File not found."); + System.exit(-1); + } + catch (IOException e) + { + System.err.println("Error while reading clustering from file: " + e.getMessage()); + System.exit(-1); + } + finally + { + if (reader != null) + try + { + reader.close(); + } + catch (IOException e) + { + System.err.println("Error while reading clustering from file: " + e.getMessage()); + System.exit(-1); + } + } + + return new Clustering(clusters); + } + + /** + * Writes a clustering to a file. + * + * @param filename Filename + * @param clustering Clustering + */ + public static void writeClustering(String filename, Clustering clustering) + { + BufferedWriter writer = null; + try + { + writer = new BufferedWriter((filename == null) ? new OutputStreamWriter(System.out) : new FileWriter(filename)); + for (int i = 0; i < clustering.getNNodes(); i++) + { + writer.write(i + COLUMN_SEPARATOR + clustering.getCluster(i)); + writer.newLine(); + } + } + catch (FileNotFoundException e) + { + System.err.println("Error while writing clustering to file: File not found."); + System.exit(-1); + } + catch (IOException e) + { + System.err.println("Error while writing clustering to file: " + e.getMessage()); + System.exit(-1); + } + finally + { + if (writer != null) + try + { + writer.close(); + } + catch (IOException e) + { + System.err.println("Error while writing clustering to file: " + e.getMessage()); + System.exit(-1); + } + } + } + + private RunNetworkClustering() + { + } +} diff --git a/src/cwts/networkanalysis/run/package-info.java b/src/cwts/networkanalysis/run/package-info.java new file mode 100644 index 0000000..0604bd3 --- /dev/null +++ b/src/cwts/networkanalysis/run/package-info.java @@ -0,0 +1,18 @@ +/** + * Provides classes for running network analysis algorithms. + * + *

+ * Currently, the focus of this package is restricted to clustering algorithms + * (also known as community detection algorithms). + *

+ * + *

+ * The network analysis algorithms are provided by the package {@link + * cwts.networkanalysis}. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +package cwts.networkanalysis.run; diff --git a/src/cwts/util/Arrays.java b/src/cwts/util/Arrays.java new file mode 100644 index 0000000..0469b0d --- /dev/null +++ b/src/cwts/util/Arrays.java @@ -0,0 +1,263 @@ +package cwts.util; + +import java.util.Random; + +/** + * Utility functions for arrays. + * + *

+ * All methods in this class are static. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public final class Arrays +{ + /** + * Calculates the sum of the values in an array. + * + * @param values Values + * + * @return Sum of values + */ + public static double calcSum(double[] values) + { + double sum; + int i; + + sum = 0; + for (i = 0; i < values.length; i++) + sum += values[i]; + return sum; + } + + /** + * Calculates the sum of the values in an array, considering only array + * elements within a specified range. + * + *

+ * The sum is calculated over the elements + * {@code values[beginIndex], ..., values[endIndex - 1]}. + *

+ * + * @param values Values + * @param beginIndex Begin index + * @param endIndex End index + * + * @return Sum of values + */ + public static double calcSum(double[] values, int beginIndex, int endIndex) + { + double sum; + int i; + + sum = 0; + for (i = beginIndex; i < endIndex; i++) + sum += values[i]; + return sum; + } + + /** + * Calculates the average of the values in an array. + * + * @param values Values + * + * @return Average value + */ + public static double calcAverage(double[] values) + { + return calcSum(values) / values.length; + } + + /** + * Calculates the median of the values in an array. + * + * @param values Values + * + * @return Median value + */ + public static double calcMedian(double[] values) + { + double median; + double[] sortedValues; + + sortedValues = values.clone(); + java.util.Arrays.sort(sortedValues); + if (sortedValues.length % 2 == 1) + median = sortedValues[(sortedValues.length - 1) / 2]; + else + median = (sortedValues[sortedValues.length / 2 - 1] + sortedValues[sortedValues.length / 2]) / 2; + return median; + } + + /** + * Calculates the minimum of the values in an array. + * + * @param values Values + * + * @return Minimum value + */ + public static double calcMinimum(double[] values) + { + double minimum; + int i; + + minimum = values[0]; + for (i = 1; i < values.length; i++) + minimum = Math.min(minimum, values[i]); + return minimum; + } + + /** + * Calculates the maximum of the values in an array. + * + * @param values Values + * + * @return Maximum value + */ + public static double calcMaximum(double[] values) + { + double maximum; + int i; + + maximum = values[0]; + for (i = 1; i < values.length; i++) + maximum = Math.max(maximum, values[i]); + return maximum; + } + + /** + * Calculates the minimum of the values in an array. + * + * @param values Values + * + * @return Minimum value + */ + public static int calcMinimum(int[] values) + { + int i, minimum; + + minimum = values[0]; + for (i = 1; i < values.length; i++) + minimum = Math.max(minimum, values[i]); + return minimum; + } + + /** + * Calculates the maximum of the values in an array. + * + * @param values Values + * + * @return Maximum value + */ + public static int calcMaximum(int[] values) + { + int i, maximum; + + maximum = values[0]; + for (i = 1; i < values.length; i++) + maximum = Math.max(maximum, values[i]); + return maximum; + } + + /** + * Creates a double array of ones. + * + * @param nElements Number of elements + * + * @return Array of ones + */ + public static double[] createDoubleArrayOfOnes(int nElements) + { + double[] values; + + values = new double[nElements]; + java.util.Arrays.fill(values, 1); + return values; + } + + /** + * Creates a double array of random numbers. + * + * @param nElements Number of elements + * + * @return Array of random numbers + */ + public static double[] createDoubleArrayOfRandomNumbers(int nElements) + { + return createDoubleArrayOfRandomNumbers(nElements, new Random()); + } + + /** + * Creates a double array of random numbers. + * + * @param nElements Number of elements + * @param random Random number generator + * + * @return Array of random numbers + */ + public static double[] createDoubleArrayOfRandomNumbers(int nElements, Random random) + { + double[] values; + int i; + + values = new double[nElements]; + for (i = 0; i < nElements; i++) + values[i] = random.nextDouble(); + return values; + } + + /** + * Generates a random permutation. + * + *

+ * A random permutation is generated of the integers + * {@code 0, ..., nElements - 1}. + *

+ * + * @param nElements Number of elements + * + * @return Random permutation + */ + public static int[] generateRandomPermutation(int nElements) + { + return generateRandomPermutation(nElements, new Random()); + } + + /** + * Generates a random permutation. + * + *

+ * A random permutation is generated of the integers + * {@code 0, ..., nElements - 1}. + *

+ * + * @param nElements Number of elements + * @param random Random number generator + * + * @return Random permutation + */ + public static int[] generateRandomPermutation(int nElements, Random random) + { + int i, j, k; + int[] permutation; + + permutation = new int[nElements]; + for (i = 0; i < nElements; i++) + permutation[i] = i; + for (i = 0; i < nElements; i++) + { + j = random.nextInt(nElements); + k = permutation[i]; + permutation[i] = permutation[j]; + permutation[j] = k; + } + return permutation; + } + + private Arrays() + { + } +} diff --git a/src/cwts/util/DynamicDoubleArray.java b/src/cwts/util/DynamicDoubleArray.java new file mode 100644 index 0000000..0a1e0a6 --- /dev/null +++ b/src/cwts/util/DynamicDoubleArray.java @@ -0,0 +1,190 @@ +package cwts.util; + +import java.util.Arrays; + +/** + * Dynamic array of doubles. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class DynamicDoubleArray implements Cloneable +{ + /** + * Default initial capacity of the array. + */ + public static final int DEFAULT_INITIAL_CAPACITY = 10; + + /** + * Relative increase in the capacity of the array when the existing + * capacity is insufficient. + */ + public static final double RELATIVE_CAPACITY_INCREASE = 0.5; + + private double[] values; + private int size; + + /** + * Constructs an empty dynamic array. + * + */ + public DynamicDoubleArray() + { + this(DEFAULT_INITIAL_CAPACITY); + } + + /** + * Constructs an empty dynamic array with a specified initial capacity. + * + * @param initialCapacity Initial capacity + */ + public DynamicDoubleArray(int initialCapacity) + { + values = new double[initialCapacity]; + size = 0; + } + + /** + * Constructs a dynamic array containing specified values. + * + * @param values Values + */ + public DynamicDoubleArray(double[] values) + { + values = values.clone(); + size = values.length; + } + + /** + * Clones the array. + * + * @return Cloned array + */ + public DynamicDoubleArray clone() + { + DynamicDoubleArray clonedArray; + + try + { + clonedArray = (DynamicDoubleArray)super.clone(); + clonedArray.values = values.clone(); + return clonedArray; + } + catch (CloneNotSupportedException e) + { + return null; + } + } + + /** + * Returns the capacity of the array. + * + * @return Capacity + */ + public int capacity() + { + return values.length; + } + + /** + * Ensures a specified minimum capacity of the array. + * + *

+ * The capacity is increased (if necessary) to ensure that it equals at + * least the specified minimum capacity. + *

+ * + * @param minCapacity Minimum capacity + */ + public void ensureCapacity(int minCapacity) + { + int newCapacity, oldCapacity; + + oldCapacity = values.length; + if (minCapacity > oldCapacity) + { + newCapacity = (int)((1 + RELATIVE_CAPACITY_INCREASE) * oldCapacity); + if (newCapacity < minCapacity) + newCapacity = minCapacity; + values = Arrays.copyOf(values, newCapacity); + } + } + + /** + * Returns the number of elements of the array. + * + * @return Size + */ + public int size() + { + return size; + } + + /** + * Returns the value of an element of the array. + * + * @param index Index + * + * @return Value + */ + public double get(int index) + { + if (index >= size) + throw new IndexOutOfBoundsException(); + + return values[index]; + } + + /** + * Sets an element of the array to a specified value. + * + * @param index Index + * @param value Value + */ + public void set(int index, double value) + { + if (index >= size) + throw new IndexOutOfBoundsException(); + + values[index] = value; + } + + /** + * Appends a specified value to the end of the array. + * + * @param value Value + */ + public void append(double value) + { + ensureCapacity(size + 1); + values[size] = value; + size++; + } + + /** + * Removes all elements from the array. + */ + public void clear() + { + size = 0; + } + + /** + * Sorts the array. + */ + public void sort() + { + Arrays.sort(values, 0, size); + } + + /** + * Returns a static array copy. + * + * @return Array + */ + public double[] toArray() + { + return Arrays.copyOf(values, size); + } +} diff --git a/src/cwts/util/DynamicIntArray.java b/src/cwts/util/DynamicIntArray.java new file mode 100644 index 0000000..97e2718 --- /dev/null +++ b/src/cwts/util/DynamicIntArray.java @@ -0,0 +1,190 @@ +package cwts.util; + +import java.util.Arrays; + +/** + * Dynamic array of integers. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public class DynamicIntArray implements Cloneable +{ + /** + * Default initial capacity of the array. + */ + public static final int DEFAULT_INITIAL_CAPACITY = 10; + + /** + * Relative increase in the capacity of the array when the existing + * capacity is insufficient. + */ + public static final double RELATIVE_CAPACITY_INCREASE = 0.5; + + private int[] values; + private int size; + + /** + * Constructs an empty dynamic array. + * + */ + public DynamicIntArray() + { + this(DEFAULT_INITIAL_CAPACITY); + } + + /** + * Constructs an empty dynamic array with a specified initial capacity. + * + * @param initialCapacity Initial capacity + */ + public DynamicIntArray(int initialCapacity) + { + values = new int[initialCapacity]; + size = 0; + } + + /** + * Constructs a dynamic array containing specified values. + * + * @param values Values + */ + public DynamicIntArray(int[] values) + { + values = values.clone(); + size = values.length; + } + + /** + * Clones the array. + * + * @return Cloned array + */ + public DynamicIntArray clone() + { + DynamicIntArray clonedArray; + + try + { + clonedArray = (DynamicIntArray)super.clone(); + clonedArray.values = values.clone(); + return clonedArray; + } + catch (CloneNotSupportedException e) + { + return null; + } + } + + /** + * Returns the capacity of the array. + * + * @return Capacity + */ + public int capacity() + { + return values.length; + } + + /** + * Ensures a specified minimum capacity of the array. + * + *

+ * The capacity is increased (if necessary) to ensure that it equals at + * least the specified minimum capacity. + *

+ * + * @param minCapacity Minimum capacity + */ + public void ensureCapacity(int minCapacity) + { + int newCapacity, oldCapacity; + + oldCapacity = values.length; + if (minCapacity > oldCapacity) + { + newCapacity = (int)((1 + RELATIVE_CAPACITY_INCREASE) * oldCapacity); + if (newCapacity < minCapacity) + newCapacity = minCapacity; + values = Arrays.copyOf(values, newCapacity); + } + } + + /** + * Returns the number of elements of the array. + * + * @return Size + */ + public int size() + { + return size; + } + + /** + * Returns the value of an element of the array. + * + * @param index Index + * + * @return Value + */ + public int get(int index) + { + if (index >= size) + throw new IndexOutOfBoundsException(); + + return values[index]; + } + + /** + * Sets an element of the array to a specified value. + * + * @param index Index + * @param value Value + */ + public void set(int index, int value) + { + if (index >= size) + throw new IndexOutOfBoundsException(); + + values[index] = value; + } + + /** + * Appends a specified value to the end of the array. + * + * @param value Value + */ + public void append(int value) + { + ensureCapacity(size + 1); + values[size] = value; + size++; + } + + /** + * Removes all elements from the array. + */ + public void clear() + { + size = 0; + } + + /** + * Sorts the array. + */ + public void sort() + { + Arrays.sort(values, 0, size); + } + + /** + * Returns a static array copy. + * + * @return Array + */ + public int[] toArray() + { + return Arrays.copyOf(values, size); + } +} diff --git a/src/cwts/util/FastMath.java b/src/cwts/util/FastMath.java new file mode 100644 index 0000000..2313ba8 --- /dev/null +++ b/src/cwts/util/FastMath.java @@ -0,0 +1,73 @@ +package cwts.util; + +/** + * Fast implementations of mathematical functions. + * + *

+ * All methods in this class are static. + *

+ * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +public final class FastMath +{ + /** + * Calculates {@code exp(exponent)} using a fast implementation. + * + * @param exponent Exponent + * + * @return exp(exponent) + */ + public static double fastExp(double exponent) + { + if (exponent < -256d) + return 0; + + exponent = 1d + exponent / 256d; + exponent *= exponent; + exponent *= exponent; + exponent *= exponent; + exponent *= exponent; + exponent *= exponent; + exponent *= exponent; + exponent *= exponent; + exponent *= exponent; + return exponent; + } + + /** + * Calculates {@code base ^ exponent} using a fast implementation. + * + * @param base Base + * @param exponent Exponent + * + * @return base ^ exponent + */ + public static double fastPow(double base, int exponent) + { + double power; + int i; + + if (exponent > 0) + { + power = base; + for (i = 1; i < exponent; i++) + power *= base; + } + else if (exponent < 0) + { + power = 1 / base; + for (i = -1; i > exponent; i--) + power /= base; + } + else + power = 1; + return power; + } + + private FastMath() + { + } +} diff --git a/src/cwts/util/package-info.java b/src/cwts/util/package-info.java new file mode 100644 index 0000000..b3ea45b --- /dev/null +++ b/src/cwts/util/package-info.java @@ -0,0 +1,8 @@ +/** + * Provides miscellaneous utility classes. + * + * @author Ludo Waltman + * @author Nees Jan van Eck + * @author Vincent Traag + */ +package cwts.util;