diff --git a/README.md b/README.md index 885b80b..c9b1ac0 100644 --- a/README.md +++ b/README.md @@ -1,265 +1,291 @@ -# Mercator -Inference of high-quality embeddings of complex networks into the hyperbolic disk - -## Table of content - -1. Installation - * [Command line executable](#command-line-executable) - * [Python module](#python-module) -2. Usage - * [Input files](#format-of-input-files) - * [Running the code](#running-the-code) - * [Ouput files](#output-files) - * [Options](#options) -3. [Publications](#publications) - - -## Installation - -Two interfaces of the program are available: a command line executable and a Python module. - - -### Command line executable - -Requirements -* A C++11 (or newer) compliant compiler -* The header `unistd.h`. - -``` -# Unix (Linux / MAC OS) -g++ -O3 -std=c++11 -I include/ src/embeddingS1_unix.cpp -o mercator -``` - - -### Python module - -Requirements -* A C++11 (or newer) compliant compiler -* python 3.x -* [pybind11] (will be installed automatically) - -``` -# Unix (Linux / MAC OS) -pip3 install . # to be executed from mercator's repository -``` - - -## Usage - - -### Format of input files - -The structure of the network to be embedded is passed to the program via a file containing its edgelist (one link per line). The edgelist file consists in a simple text file with the following convention - -``` -# lines beginning with "#" are ignored (comments). -# note that nodes' name must be separated by at least one white space. -# there may be white space at the beginning of a line. -[name of node1] [name of node2] [remaining information will be ignored] -[name of node2] [name of node3] [remaining information will be ignored] -[name of node4] [name of node5] [remaining information will be ignored] -# comments can be inserted between links -[name of node5] [name of node6] [remaining information will be ignored] -[name of node7] [name of node6] [remaining information will be ignored] -... -``` - -Note that the nodes' name will be imported as `std::string` and can therefore be virtually anything as long as they do not include white spaces (i.e., there is not need for the nodes to be identified by contiguous integers). - -**IMPORTANT**: this class only considers **simple undirected** networks **without self-loops**. Any multiple edges (e.g., if the graph is originally directed) or self-loops will be ignored. - -**IMPORTANT**: in the actual version of the code, the network must have **only one component**. - - -### Running the code - -Running `mercator` is quite straightforward -``` -# Command line -./mercator - -# Python module -mercator.embed() -``` - - -### Output files - -The program outputs several files during and after the embedding procedure - -* `*.inf_coord`: Contains information about the embedding procedure, the inferred parameters and the inferred positions. -* `*.inf_log`: Contains detailled information about the embedding procedure. - - -### Options - -Several options are provided to adjust the embedding procedure to specific needs and can be combined - -* [Custom output filename](#custom-output-filename) -* [Custom value for beta](#custom-value-for-beta) -* [Custom value for the seed of the random number generator](#custom-value-for-the-seed-of-the-random-number-generator) -* [Clean output mode](#clean-output-mode) -* [Fast mode](#fast-mode) -* [Post-processing of the inferred values of the radial positions](#post-processing-of-the-inferred-values-of-the-radial-positions) -* [Quiet mode](#quiet-mode) -* [Refine mode](#refine-mode) -* [Screen mode](#screen-mode) -* [Validation mode](#validation-mode) - -#### Custom output filename - -All generated files are named `.` and a custom `` can be provided. If none is provided, the `` is extracted from the `` by removing its extension, otherwise the full `` is used as `` if ``does not have any extension. - -``` -# Command line -./mercator -o - -# Python module -mercator.embed(, output_name=) -``` - -#### Custom value for beta - -A custom value for the parameter `beta` can be provided. By default a value for beta is inferred to reproduce the average local clustering coefficient of the original edgelist. - -``` -# Command line -./mercator -b - -# Python module -mercator.embed(, beta=) -``` - -#### Custom value for the seed of the random number generator - -A custom seed for the random number generator can be provided (useful when several embeddings are performed in parallel). By default, `EPOCH` is used. - -``` -# Command line -./mercator -s - -# Python module -mercator.embed(, seed=) -``` - -#### Clean output mode - -Outputs a file with extension `*.inf_coord_raw` containing the columns 2, 3 and 4 of the file with extension `*.inf_coord`. Rows follow the same order as in the file with extension `*.inf_coord`. The global parameters (i.e., beta, mu, etc.) ate not included in the file. Default is **`false`**. - -``` -# Command line -./mercator -c - -# Python module -mercator.embed(, clean_mode=True) -``` - - -#### Fast mode - -Skip the likelihood maximization step (i.e., only infers the positions using the EigenMap methods). Default is **`false`**. - -``` -# Command line -./mercator -f - -# Python module -mercator.embed(, fast_mode=True) -``` - -#### Post-processing of the inferred values of the radial positions - -The inferred radial positions are updated based on the inferred angular positions. When deactivated, nodes with the same degree have the same radial position in the hyperbolic disk. Default is **`true`**. - -``` -# Command line -./mercator -k - -# Python module -mercator.embed(, post_kappa=False) -``` - -#### Quiet mode - -The program does not output details about the progress of the embedding procedure (i.e., the file `*.inf_log` is not generated). This mode supersedes the *verbose* mode (i.e., no output on screen). Default is **`false`**. - -``` -# Command line -./mercator -q - -# Python module -mercator.embed(, quiet_mode=True) -``` - -#### Refine mode - -When a file containing the previously inferred coordinates is provided (`*.inf_coord`), the program uses the already inferred positions and parameters as a starting point and perform another round of the likelihood maximization step to refine the inferred positions. The use of a different output filename is recommended to keep track of the changes. Default is **`false`**. - -``` -# Command line -./mercator -r - -# Python module -mercator.embed(, inf_coord=) -``` - -#### Screen mode - -The program outputs details about the progress of the embedding procedure on screen instead of generating the file `*.inf_log`. Default is **`false`**. - -``` -# Command line -./mercator -a - -# Python module -mercator.embed(, screen_mode=True) -``` - -#### Validation mode - -Validates and characterizes the inferred random network ensemble. This is done by generating a large number of networks based on the inferred parameters and positions. The following files are generated - -* `*.inf_inf_pconn`: Compares the inferred probability of connection with the theoretical one based on the inferred parameters. -* `*.inf_theta_density`: Density the angular distribution -* `*.inf_vprop`: Contains the following topological properties for every nodes in the inferred ensemble and in the original network: - * degree - * sum of the degree of the neighbors - * average degree of the neighbors - * number of triangles - * local clustering coefficient -* `*.inf_vstat`: Contains the following statistics of the inferred ensemble. - * degree distribution - * spectrum of the average degree of neighbors - * clustering spectrum -* `*.obs_vstat`: Contains the same statistics as above but for the original network. - -Default is **`false`**. A python script `plot_validation_of_embedding.py` is provided to visualize the results of the validation mode. - -``` -# Command line -./mercator -v - -# Python module -mercator.embed(, validation_mode=True) - -# To plot the various characterization quantities -python3 scripts/plot_validation_of_embedding.py -``` - - -## Publications - -Please cite: - -_Mercator: uncovering faithful hyperbolic embeddings of complex networks_
-[Guillermo García-Pérez*](https://scholar.google.es/citations?user=MibFSJIAAAAJ&hl=en), -[Antoine Allard*](http://antoineallard.info), -[M. Ángeles Serrano](http://morfeo.ffn.ub.es/~mariangeles/ws_en/) and -[Marián Boguñá](http://complex.ffn.ub.es/~mbogunya/)
-New Journal of Physics 21, 123033 (2019)
-[Full text](https://doi.org/10.1088/1367-2630/ab57d2) | [arXiv](https://arxiv.org/abs/1904.10814) - - - - -[pybind11]: https://github.com/pybind/pybind11 +# Mercator +Inference of high-quality embeddings of complex networks into the hyperbolic disk + +## Table of content + +1. Installation + * [Command line executable](#command-line-executable) + * [Python module](#python-module) +2. Usage + * [Input files](#format-of-input-files) + * [Running the code](#running-the-code) + * [Ouput files](#output-files) + * [Options](#options) +3. [Publications](#publications) + + +## Installation + +Two interfaces of the program are available: a command line executable and a Python module. + + +### Command line executable + +Requirements +* A C++11 (or newer) compliant compiler +* The header `unistd.h`. + +``` +# Unix (Linux / MAC OS) +g++ -O3 -std=c++11 -I include/ src/embeddingS1_unix.cpp -o mercator +``` + + +### Python module + +Requirements +* A C++11 (or newer) compliant compiler +* python 3.x +* [pybind11] (will be installed automatically) + +``` +# Unix (Linux / MAC OS) +pip3 install . # to be executed from mercator's repository +``` + + +## Usage + + +### Format of input files + +The structure of the network to be embedded is passed to the program via a file containing its edgelist (one link per line). The edgelist file consists in a simple text file with the following convention + +``` +# lines beginning with "#" are ignored (comments). +# note that nodes' name must be separated by at least one white space. +# there may be white space at the beginning of a line. +[name of node1] [name of node2] [remaining information will be ignored] +[name of node2] [name of node3] [remaining information will be ignored] +[name of node4] [name of node5] [remaining information will be ignored] +# comments can be inserted between links +[name of node5] [name of node6] [remaining information will be ignored] +[name of node7] [name of node6] [remaining information will be ignored] +... +``` + +Note that the nodes' name will be imported as `std::string` and can therefore be virtually anything as long as they do not include white spaces (i.e., there is not need for the nodes to be identified by contiguous integers). + +**IMPORTANT**: this class only considers **simple undirected** networks **without self-loops**. Any multiple edges (e.g., if the graph is originally directed) or self-loops will be ignored. + +**IMPORTANT**: in the actual version of the code, the network must have **only one component**. + + +### Running the code + +Running `mercator` is quite straightforward +``` +# Command line +./mercator + +# Python module +mercator.embed() +``` + + +### Output files + +The program outputs several files during and after the embedding procedure + +* `*.inf_coord`: Contains information about the embedding procedure, the inferred parameters and the inferred positions. +* `*.inf_log`: Contains detailled information about the embedding procedure. + + +### Options + +Several options are provided to adjust the embedding procedure to specific needs and can be combined + +* [Custom output filename](#custom-output-filename) +* [Custom value for beta](#custom-value-for-beta) +* [Custom value for the seed of the random number generator](#custom-value-for-the-seed-of-the-random-number-generator) +* [Clean output mode](#clean-output-mode) +* [Fast mode](#fast-mode) +* [Post-processing of the inferred values of the radial positions](#post-processing-of-the-inferred-values-of-the-radial-positions) +* [Quiet mode](#quiet-mode) +* [Refine mode](#refine-mode) +* [Screen mode](#screen-mode) +* [Validation mode](#validation-mode) +* [All beta mode](#all-beta-mode) +* [Numeric mu mode](#numeric-mu-mode) + +#### Custom output filename + +All generated files are named `.` and a custom `` can be provided. If none is provided, the `` is extracted from the `` by removing its extension, otherwise the full `` is used as `` if ``does not have any extension. + +``` +# Command line +./mercator -o + +# Python module +mercator.embed(, output_name=) +``` + +#### Custom value for beta + +A custom value for the parameter `beta` can be provided. By default a value for beta is inferred to reproduce the average local clustering coefficient of the original edgelist. + +``` +# Command line +./mercator -b + +# Python module +mercator.embed(, beta=) +``` + +#### Custom value for the seed of the random number generator + +A custom seed for the random number generator can be provided (useful when several embeddings are performed in parallel). By default, `EPOCH` is used. + +``` +# Command line +./mercator -s + +# Python module +mercator.embed(, seed=) +``` + +#### Clean output mode + +Outputs a file with extension `*.inf_coord_raw` containing the columns 2, 3 and 4 of the file with extension `*.inf_coord`. Rows follow the same order as in the file with extension `*.inf_coord`. The global parameters (i.e., beta, mu, etc.) ate not included in the file. Default is **`false`**. + +``` +# Command line +./mercator -c + +# Python module +mercator.embed(, clean_mode=True) +``` + + +#### Fast mode + +Skip the likelihood maximization step (i.e., only infers the positions using the EigenMap methods). Default is **`false`**. + +``` +# Command line +./mercator -f + +# Python module +mercator.embed(, fast_mode=True) +``` + +#### Post-processing of the inferred values of the radial positions + +The inferred radial positions are updated based on the inferred angular positions. When deactivated, nodes with the same degree have the same radial position in the hyperbolic disk. Default is **`true`**. + +``` +# Command line +./mercator -k + +# Python module +mercator.embed(, post_kappa=False) +``` + +#### Quiet mode + +The program does not output details about the progress of the embedding procedure (i.e., the file `*.inf_log` is not generated). This mode supersedes the *verbose* mode (i.e., no output on screen). Default is **`false`**. + +``` +# Command line +./mercator -q + +# Python module +mercator.embed(, quiet_mode=True) +``` + +#### Refine mode + +When a file containing the previously inferred coordinates is provided (`*.inf_coord`), the program uses the already inferred positions and parameters as a starting point and perform another round of the likelihood maximization step to refine the inferred positions. The use of a different output filename is recommended to keep track of the changes. Default is **`false`**. + +``` +# Command line +./mercator -r + +# Python module +mercator.embed(, inf_coord=) +``` + +#### Screen mode + +The program outputs details about the progress of the embedding procedure on screen instead of generating the file `*.inf_log`. Default is **`false`**. + +``` +# Command line +./mercator -a + +# Python module +mercator.embed(, screen_mode=True) +``` + +#### Validation mode + +Validates and characterizes the inferred random network ensemble. This is done by generating a large number of networks based on the inferred parameters and positions. The following files are generated + +* `*.inf_inf_pconn`: Compares the inferred probability of connection with the theoretical one based on the inferred parameters. +* `*.inf_theta_density`: Density the angular distribution +* `*.inf_vprop`: Contains the following topological properties for every nodes in the inferred ensemble and in the original network: + * degree + * sum of the degree of the neighbors + * average degree of the neighbors + * number of triangles + * local clustering coefficient +* `*.inf_vstat`: Contains the following statistics of the inferred ensemble. + * degree distribution + * spectrum of the average degree of neighbors + * clustering spectrum +* `*.obs_vstat`: Contains the same statistics as above but for the original network. + +Default is **`false`**. A python script `plot_validation_of_embedding.py` is provided to visualize the results of the validation mode. + +``` +# Command line +./mercator -v + +# Python module +mercator.embed(, validation_mode=True) + +# To plot the various characterization quantities +python3 scripts/plot_validation_of_embedding.py +``` + +#### All beta mode + +Allows for all $0\leq \beta$. If turned off, the condition $\beta > 1$ is imposed, thus ignoring the weakly geometric phase at $\beta < 1$. Default is **`true`**. + +``` +# Command line +./mercator -g +``` + +#### Numeric mu mode + +Uses a numerical computation of $\mu$ that takes finite size effects into account. If turned off, the analytic approximation for $N\gg 1$ is used. Default is **`true`**. + +``` +# Command line +./mercator -m +``` + +## Publications + +Please cite: + +_Mercator: uncovering faithful hyperbolic embeddings of complex networks_
+[Guillermo García-Pérez*](https://scholar.google.es/citations?user=MibFSJIAAAAJ&hl=en), +[Antoine Allard*](http://antoineallard.info), +[M. Ángeles Serrano](http://morfeo.ffn.ub.es/~mariangeles/ws_en/) and +[Marián Boguñá](http://complex.ffn.ub.es/~mbogunya/)
+New Journal of Physics 21, 123033 (2019)
+[Full text](https://doi.org/10.1088/1367-2630/ab57d2) | [arXiv](https://arxiv.org/abs/1904.10814) + +_Random graphs and real networks with weak geometric coupling_
+[Jasper van der Kolk](https://scholar.google.nl/citations?user=rCP0ljsAAAAJ&hl=en), +[M. Ángeles Serrano](http://morfeo.ffn.ub.es/~mariangeles/ws_en/) and +[Marián Boguñá](http://complex.ffn.ub.es/~mbogunya/)
+Phys. Rev. Research 6, 013337 (2024)
+[Full text](https://doi.org/10.1103/PhysRevResearch.6.013337) | [arXiv](https://doi.org/10.48550/arXiv.2312.07416) + + + + +[pybind11]: https://github.com/pybind/pybind11 diff --git a/include/embeddingS1.hpp b/include/embeddingS1.hpp index 35b52a6..bb2c061 100644 --- a/include/embeddingS1.hpp +++ b/include/embeddingS1.hpp @@ -1,3715 +1,3986 @@ -/* - * - * This class provides the functions to embed a graph in the S1 space. - * - * Compilation requires the c++11 standard (to use #include ), the Eigen, the Spectra as - * well as the hyp2f1.a library. - * - * Author: Antoine Allard - * WWW: antoineallard.info - * Version: 1.0 - * Date: May 2018 - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - */ - -#ifndef EMBEDDINGS1_HPP_INCLUDED -#define EMBEDDINGS1_HPP_INCLUDED - -// Standard Template Library -#include -// #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// Eigen library -#include "Eigen/Core" -#include "Eigen/SparseCore" -// Spectra library -#include "Spectra/GenEigsSolver.h" -#include "Spectra/MatOp/SparseGenMatProd.h" -// Custom library for specific Gaussian hypergeometric functions. -#include "hyp2f1.hpp" - - -class embeddingS1_t -{ - private: - // pi - const double PI = 3.141592653589793238462643383279502884197; - // Flags controlling options. - public: - // Characterizes the inferred ensemble (generates CHARACTERIZATION_NB_GRAPHS graphs and measure - // various structural properties). - bool CHARACTERIZATION_MODE = false; - // Also write a raw file containing the inferred coordinates without any "commented" lines. - bool CLEAN_RAW_OUTPUT_MODE = false; - // Has a custom value for beta been provided? - bool CUSTOM_BETA = false; - // Indicates whether the number of graphs generated during the characterization phase is the default value ot not. - bool CUSTOM_CHARACTERIZATION_NB_GRAPHS = false; - // Using already inferred coordinates. - bool CUSTOM_INFERRED_COORDINATES = false; - // Has a custom name for various output files generated by the program been provided? - bool CUSTOM_OUTPUT_ROOTNAME_MODE = false; - // Has a custom value for the seed been provided? - bool CUSTOM_SEED = false; - // Will the kappas be adjusted after the angular positions inferred? - bool KAPPA_POST_INFERENCE_MODE = true; - // Will or will not position the vertices to maximize the log-likelihood. - bool MAXIMIZATION_MODE = true; - // Does not provide any information during the embedding process. - bool QUIET_MODE = false; - // Refining only the already inferred positions. - bool REFINE_MODE = false; - // Provides various files that characterize the inferred ensemble. - bool VALIDATION_MODE = false; - // Print information about the embedding process on screen instead than on the log file. - bool VERBOSE_MODE = false; - - // Global parameters. - public: - // Name of the file containing the previously inferred parameters. - std::string ALREADY_INFERRED_PARAMETERS_FILENAME; - // Minimal/maximal value of beta that the program can handle (bounds). - double BETA_ABS_MAX = 25; - double BETA_ABS_MIN = 1.01; - // Number of graphs to generate during the characterization of the inferred ensemble. - int CHARACTERIZATION_NB_GRAPHS = 100; - // Number of new positions to try. - int MIN_NB_ANGLES_TO_TRY = 100; - // // Parameter governing the refinied search for optimal position during the maximization phase. - // int CLOSE_ANGULAR_RANGE_FACTOR = 2; - // Edgelist filename. - std::string EDGELIST_FILENAME; - // Number of points for MC integration in the calculation of expected clustering. - int EXP_CLUST_NB_INTEGRATION_MC_STEPS = 600; - // Number of steps for integration for the expected distance between adjacent vertices. - int EXP_DIST_NB_INTEGRATION_STEPS = 1000; - // Maximal number of attempts to reach convergence of the updated values of kappa. - int KAPPA_MAX_NB_ITER_CONV = 500; - // // Criterion for the change of the nature of the criterion for the convergence during the - // // maximization of the angular positions. - // int LIMIT_FOR_CONVERGENCE_CRITERION = 625; - // Criterion for changing the calculation of the log-likelihood. - // int NB_VERTICES_IN_CORE = 1000; - // Maximal number of steps in the maximization phase. - // int MAX_NB_ITER_MAXIMIZATION = 15; - // // Maximal angular step when looking for optimal position. - // int MINIMAL_ANGULAR_RESOLUTION = 100; - // Minimal value for the convergence of maximization. - // double MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD = 0.005; - // Minimal values for 2 sigmas when drawing new angles to test. - double MIN_TWO_SIGMAS_NORMAL_DIST = PI / 6; - // Various numerical/convergence thresholds. - double NUMERICAL_CONVERGENCE_THRESHOLD_1 = 1e-2; - double NUMERICAL_CONVERGENCE_THRESHOLD_2 = 5e-5; - double NUMERICAL_CONVERGENCE_THRESHOLD_3 = 0.5; - // double MAXIMIZATION_CONVERGENCE_THRESHOLD = 0.01; - double NUMERICAL_ZERO = 1e-10; - // // Parameter governing the refinied search for optimal position during the maximization phase. - // int REFINED_MAX_STEP_LENGTH_DIVISOR = 5; - // Rootname of output files. - std::string ROOTNAME_OUTPUT; - // Random number generator seed. - int SEED; - private: - // Version of the code. - std::string VERSION = "0.9"; - // Tab. - std::string TAB = " "; - - // General internal objects. - private: - // Random number generator - std::mt19937 engine; - std::uniform_real_distribution uniform_01; - std::normal_distribution normal_01; - // Objects mapping the name and the numerical ID of vertices. - std::map< std::string, int > Name2Num; - std::vector Num2Name; - // List of degree classes. - std::set degree_class; - // Cumulative probability used for the calculation of clustering using MC integration. - std::map< int, std::map< double, int, std::less > > cumul_prob_kgkp; - // List containing the order in which the vertices will be considered in the maximization phase. - std::vector ordered_list_of_vertices; - // Time stamps. - double time0, time1, time2, time3, time4, time5, time6, time7; - time_t time_started, time_ended; - // Stream used to output the log to file. - std::ofstream logfile; - std::streambuf *old_rdbuf; - // Widths of the columns in output file. - int width_names; - int width_values; - - // Objects related to the original graph. - private: - // Number of vertices. - int nb_vertices; - int nb_vertices_degree_gt_one; - // Number of edges. - int nb_edges; - // Average degree. - double average_degree; - // Average local clustering coefficient. - double average_clustering; - // Average degree of neighbors. - std::vector sum_degree_of_neighbors; - // Local clustering. - std::vector nbtriangles; - // Adjacency list. - std::vector< std::set > adjacency_list; - // Degree. - std::vector degree; - // ID of vertices in each degree class. - std::map > degree2vertices; - - // Objects related to the inferred graph ensemble. - public: - // Parameter beta (clustering). - double beta; - private: - // Average degree of the inferred ensemble. - double random_ensemble_average_degree; - // Average local clustering coefficient of the inferred ensemble. - double random_ensemble_average_clustering; - // Maps containing the expected degree of each degree class. - std::map random_ensemble_expected_degree_per_degree_class; - // Expected degrees in the inferred ensemble (analytical, no finite-size effect). - std::vector inferred_ensemble_expected_degree; - // List of kappas by degree class. - std::map random_ensemble_kappa_per_degree_class; - // Parameter mu (average degree). - double mu; - // Hidden variables of the vertices. - std::vector kappa; - // Positions of the vertices. - std::vector theta; - // sinus and cosinus of theta. - // std::vector sin_theta; - // std::vector cos_theta; - - // Objects related to the characterization of the inferred ensemble (by simulations). - private: - // Simulated adjacency list. - std::vector< std::set > simulated_adjacency_list; - // Simulated degrees (one instance). - std::vector simulated_degree; - // Simulated average degree of neighbors (one instance). - std::vector simulated_sum_degree_of_neighbors; - // Simulated clustering coefficients (one instance). - std::vector simulated_nb_triangles; - // Simulated degree distribution (one instance). - std::map simulated_stat_degree; - // Simulated average sum of degree of neighbors by degree class. - std::map simulated_stat_sum_degree_neighbors; - // Simulated average average degree of neighbors by degree class. - std::map simulated_stat_avg_degree_neighbors; - // Simulated average number of triangles gathered by degree class. - std::map simulated_stat_nb_triangles; - // Simulated average clustering coefficient by degree class. - std::map simulated_stat_clustering; - // Characterization of vertices (all instances). - std::vector< std::vector< std::vector > > characterizing_inferred_ensemble_vprops; - // Vertices statistics characterization (all instances). - std::map< int, std::vector > characterizing_inferred_ensemble_vstat; - - // Internal functions. - private: - // === Initialization === - // Extracts all relevant information about the degrees. - void analyze_degrees(); - // Computes the average local clustering coefficient. - void compute_clustering(); - // Loads the graph from an edgelist in a file. - void load_edgelist(); - // Loads the already inferred parameters. - void load_already_inferred_parameters(); - // === Infering parameters === - // Builds the cumulative distribution to choose degree classes in the calculation of clustering. - void build_cumul_dist_for_mc_integration(); - // Computes various properties of the random ensemble (before finding optimal positions). - void compute_random_ensemble_average_degree(); - void compute_random_ensemble_clustering(); - double compute_random_ensemble_clustering_for_degree_class(int d1); - // Infers the values of kappa. - void infer_kappas_given_beta_for_all_vertices(); - void infer_kappas_given_beta_for_degree_class(); - // === Embedding === - // Computes the log-likelihood between two vertices. - // double compute_pairwise_loglikelihood(int v1, double t1, int v2, double t2); - double compute_pairwise_loglikelihood(int v1, double t1, int v2, double t2, bool neighbors); - // Finds the initial ordering of vertices based on the Eigen Map method. - void find_initial_ordering(std::vector &ordering, std::vector &raw_theta); - void infer_initial_positions(); - // Maximizes the log-likelihood by moving the vertices. - // double find_optimal_angle(int v1); - // double find_approximate_optimal_angle(int v1); - void refine_positions(); - int refine_angle(int v1); - // void refine_angle2(int v1); - // void refine_angle3(int v1); - // void refine_angle4(int v1); - // void refine_angle5(int v1); - // void infer_optimal_positions(); - void finalize(); - // Loads the network and computes topological properties. - void initialize(); - // Infers the parameters (kappas, beta) prior to the embedding. - void infer_parameters(); - // Extracts the onion decomposition (OD) of the graph and orders the vertices according to it. - void order_vertices(); - // Updates the value of the expected degrees given the inferred positions of theta. - void compute_inferred_ensemble_expected_degrees(); - // === Characterization of the inferred ensemble using simulations === - void analyze_simulated_adjacency_list(); - void generate_simulated_adjacency_list(); - // === Miscellaneous === - // Extracts the onion decomposition. - void extract_onion_decomposition(std::vector &coreness, std::vector &od_layer); - // Gets and format current date/time. - std::string format_time(time_t _time); - // Gets the time since epoch in seconds with decimals. - double time_since_epoch_in_seconds(); - // === Output === - // Writes the inferred connection probability into a file. - void save_inferred_connection_probability(); - // Writes the inferred properties of vertices into a file. - void save_inferred_ensemble_characterization(); - // Writes the inferred theta density into a file. - void save_inferred_theta_density(); - // Writes the inferred info into a file. - void save_inferred_coordinates(); - // Function associated to the extraction of the components. - int get_root(int i, std::vector &clust_id); - void merge_clusters(std::vector &size, std::vector &clust_id); - void check_connected_components(); - - // Public functions to perform the embeddings. - public: - // Constructor (empty). - embeddingS1_t() {}; - // Destructor (empty). - ~embeddingS1_t() {}; - // Performs the embedding. - void embed(); - void embed(std::string edgelist_filename) { EDGELIST_FILENAME = edgelist_filename; embed(); }; -}; - - - - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::analyze_degrees() -{ - // Resets the value of the average degree. - average_degree = 0; - // Resets the number of vertices with a degree greater than 1. - nb_vertices_degree_gt_one = 0; - // Resizes the list of degrees. - degree.clear(); - degree.resize(nb_vertices); - if(VALIDATION_MODE) - { - sum_degree_of_neighbors.clear(); - sum_degree_of_neighbors.resize(nb_vertices, 0); - } - // Populates the list of degrees, the average degree and - // the list of vertices of each degree class. - std::set::iterator it, end; - for(int n(0), k; n 1) - { - ++nb_vertices_degree_gt_one; - } - if(VALIDATION_MODE) - { - it = adjacency_list[n].begin(); - end = adjacency_list[n].end(); - for(; it!=end; ++it) - { - sum_degree_of_neighbors[*it] += k; - } - } - } - // Completes the computation of the average degree. - average_degree /= nb_vertices; -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::analyze_simulated_adjacency_list() -{ - // Initializes the containers. - simulated_degree.clear(); - simulated_degree.resize(nb_vertices); - simulated_sum_degree_of_neighbors.clear(); - simulated_sum_degree_of_neighbors.resize(nb_vertices, 0); - simulated_nb_triangles.clear(); - simulated_nb_triangles.resize(nb_vertices, 0); - simulated_stat_degree.clear(); - simulated_stat_sum_degree_neighbors.clear(); - simulated_stat_avg_degree_neighbors.clear(); - simulated_stat_nb_triangles.clear(); - simulated_stat_clustering.clear(); - // Analyzes the degree and the average degree of neighbors. - std::set::iterator it, end; - for(int v1(0), d1; v1 intersection; - // Set objects. - std::set neighbors_v2; - // Iterator objects. - std::set::iterator it1, end1, it2, end2; - std::map >::iterator it3, end3; - std::vector::iterator it4; - // Computes the intersection for the in- and out- neighbourhoods of each vertex. - for(int v1(0), d1; v1 1. - d1 = simulated_degree[v1]; - if( d1 > 1 ) - { - // Loops over the neighbors of vertex v1. - it1 = simulated_adjacency_list[v1].begin(); - end1 = simulated_adjacency_list[v1].end(); - for(; it1!=end1; ++it1) - { - // Performs the calculation only if degree > 1. - if( simulated_degree[*it1] > 1 ) - { - // Builds an ordered list of the neighbourhood of v2 - it2 = simulated_adjacency_list[*it1].begin(); - end2 = simulated_adjacency_list[*it1].end(); - neighbors_v2.clear(); - for(; it2!=end2; ++it2) - { - if(*it1 < *it2) // Ensures that triangles will be counted only once. - { - neighbors_v2.insert(*it2); - } - } - // Counts the number of triangles. - if(neighbors_v2.size() > 0) - { - intersection.clear(); - intersection.resize(std::min(simulated_adjacency_list[v1].size(), neighbors_v2.size())); - it4 = std::set_intersection(simulated_adjacency_list[v1].begin(), simulated_adjacency_list[v1].end(), - neighbors_v2.begin(), neighbors_v2.end(), intersection.begin()); - intersection.resize(it4-intersection.begin()); - nb_triangles += intersection.size(); - } - } - } - // Adds the contribution of vertex v1 to the average clustering coefficient. - // tmp_val = 2 * nb_triangles / (d1 * (d1 - 1)); - simulated_nb_triangles[v1] = nb_triangles; - // simulated_nb_triangles[v1] = tmp_val; - } - } - // - for(int v1(0), d1; v1 0) - { - simulated_stat_sum_degree_neighbors[d1] += simulated_sum_degree_of_neighbors[v1]; - simulated_stat_avg_degree_neighbors[d1] += simulated_sum_degree_of_neighbors[v1] / d1; - } - if(d1 > 1) - { - simulated_stat_nb_triangles[d1] += simulated_nb_triangles[v1]; - simulated_stat_clustering[d1] += 2 * simulated_nb_triangles[v1] / d1 / (d1 - 1); - } - } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::build_cumul_dist_for_mc_integration() -{ - // Variables. - int v1; - double tmp_val, tmp_cumul; - // Parameters. - double R = nb_vertices / (2 * PI); - mu = beta * std::sin(PI / beta) / (2.0 * PI * average_degree); - // Temporary container. - std::map nkkp; - // Resets the main object. - cumul_prob_kgkp.clear(); - // Iterator objects. - std::set::iterator it1, end1, it2, end2; - // For all degree classes over 1. - it1 = degree_class.begin(); - end1 = degree_class.end(); - while(*it1 < 2) { ++it1; } - for(; it1!=end1; ++it1) - { - // Reinitializes the temporary container. - nkkp.clear(); - - // For all degree classes. - it2 = degree_class.begin(); - end2 = degree_class.end(); - for(; it2!=end2; ++it2) - { - // Initializes the temporary container. - nkkp[*it2] = 0; - } - - // For all degree classes. - it2 = degree_class.begin(); - end2 = degree_class.end(); - for(; it2!=end2; ++it2) - { - tmp_val = hyp2f1a(beta, -std::pow((PI * R) / (mu * random_ensemble_kappa_per_degree_class[*it1] * random_ensemble_kappa_per_degree_class[*it2]), beta)); - nkkp[*it2] = degree2vertices[*it2].size() * tmp_val / random_ensemble_expected_degree_per_degree_class[*it1]; - } - - // Initializes the cumulating variable. - tmp_cumul = 0; - // Initializes the sub-container. - cumul_prob_kgkp[*it1]; - // For all degree classes. - it2 = degree_class.begin(); - end2 = degree_class.end(); - for(; it2!=end2; ++it2) - { - - tmp_val = nkkp[*it2]; - if(tmp_val > NUMERICAL_ZERO) - { - // Cumulates the probabilities; - tmp_cumul += tmp_val; - // Builds the cumulative distribution. - cumul_prob_kgkp[*it1][tmp_cumul] = *it2; - } - } - } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::compute_clustering() -{ - // Variables. - double nb_triangles, tmp_val; - // Vector objects. - std::vector intersection; - // Set objects. - std::set neighbors_v2; - // Iterator objects. - std::vector::iterator it; - std::set::iterator it1, end1, it2, end2; - std::map >::iterator it3, end3; - if(VALIDATION_MODE) - { - // Initializes the individual clustering coefficients. - nbtriangles.clear(); - nbtriangles.resize(nb_vertices, 0); - } - // Computes the intersection for the in- and out- neighbourhoods of each vertex. - for(int v1(0), d1; v1 1. - d1 = degree[v1]; - if( d1 > 1 ) - { - // Loops over the neighbors of vertex v1. - it1 = adjacency_list[v1].begin(); - end1 = adjacency_list[v1].end(); - for(; it1!=end1; ++it1) - { - // Performs the calculation only if degree > 1. - if( degree[*it1] > 1 ) - { - // Builds an ordered list of the neighbourhood of v2 - it2 = adjacency_list[*it1].begin(); - end2 = adjacency_list[*it1].end(); - neighbors_v2.clear(); - for(; it2!=end2; ++it2) - { - if(*it1 < *it2) // Ensures that triangles will be counted only once. - { - neighbors_v2.insert(*it2); - } - } - // Counts the number of triangles. - if(neighbors_v2.size() > 0) - { - intersection.clear(); - intersection.resize(std::min(adjacency_list[v1].size(), neighbors_v2.size())); - it = std::set_intersection(adjacency_list[v1].begin(), adjacency_list[v1].end(), - neighbors_v2.begin(), neighbors_v2.end(), intersection.begin()); - intersection.resize(it-intersection.begin()); - nb_triangles += intersection.size(); - } - } - } - // Adds the contribution of vertex v1 to the average clustering coefficient. - tmp_val = 2 * nb_triangles / (d1 * (d1 - 1)); - average_clustering += tmp_val; - if(VALIDATION_MODE) - { - nbtriangles[v1] = nb_triangles; - } - } - } - // Completes the calculation of the average local clustering coefficient. - average_clustering /= nb_vertices_degree_gt_one; -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::compute_inferred_ensemble_expected_degrees() -{ - // Variables. - double kappa1, theta1, dtheta, prob; - double prefactor = nb_vertices / (2 * PI * mu); - // Computes the new expected degrees given the inferred values of theta. - inferred_ensemble_expected_degree.clear(); - inferred_ensemble_expected_degree.resize(nb_vertices, 0); - for(int v1(0); v1::iterator it2 = random_ensemble_expected_degree_per_degree_class.begin(); - std::map::iterator end2 = random_ensemble_expected_degree_per_degree_class.end(); - for(; it2!=end2; ++it2) - { - random_ensemble_average_degree += it2->second * degree2vertices[it2->first].size(); - } - random_ensemble_average_degree /= nb_vertices; -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::compute_random_ensemble_clustering() -{ - // Reinitializes the average clustering for the ensemble. - random_ensemble_average_clustering = 0; - // Computes the inferred ensemble clustering spectrum for all degree classes over 1. - std::set::iterator it = degree_class.begin(); - std::set::iterator end = degree_class.end(); - while(*it < 2) { ++it; } - for(double p23; it!=end; ++it) - { - // Computes the clustering coefficient for the degree class and updates the average value. - p23 = compute_random_ensemble_clustering_for_degree_class(*it); - // ensemble_clustering_spectrum[*it] = p23; - random_ensemble_average_clustering += p23 * degree2vertices[*it].size(); - } - // Completes the calculation of the average clustering coefficient of the inferred ensemble. - random_ensemble_average_clustering /= nb_vertices_degree_gt_one; -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -double embeddingS1_t::compute_random_ensemble_clustering_for_degree_class(int d1) -{ - // Variables. - int d2, d3; - double p12, p13, pc, pz, zmin, zmax, z, z12, z13, da, tmp; - double p23 = 0; - // Parameters. - int nb_points = EXP_CLUST_NB_INTEGRATION_MC_STEPS; - double R = nb_vertices / (2 * PI); - mu = beta * std::sin(PI / beta) / (2.0 * PI * average_degree); - // MC integration. - for(int i(0); isecond; - // Computes their probability of being connected. - p12 = hyp2f1a(beta, -std::pow((PI * R) / (mu * random_ensemble_kappa_per_degree_class[d1] * random_ensemble_kappa_per_degree_class[d2]), beta)); - - // Gets the degree of vertex 3; - d3 = cumul_prob_kgkp[d1].lower_bound(uniform_01(engine))->second; - // Computes their probability of being connected. - p13 = hyp2f1a(beta, -std::pow((PI * R) / (mu * random_ensemble_kappa_per_degree_class[d1] * random_ensemble_kappa_per_degree_class[d3]), beta)); - - // - pc = uniform_01(engine); - zmin = 0; - zmax = PI; - while( (zmax - zmin) > NUMERICAL_CONVERGENCE_THRESHOLD_2 ) - { - z = (zmax + zmin) / 2; - pz = (z / PI) * hyp2f1a(beta, -std::pow( (z * R) / (mu * random_ensemble_kappa_per_degree_class[d1] * random_ensemble_kappa_per_degree_class[d2]), beta)) / p12; - if(pz > pc) - { - zmax = z; - } - else - { - zmin = z; - } - } - z12 = (zmax + zmin) / 2; - - // - pc = uniform_01(engine); - zmin = 0; - zmax = PI; - while( (zmax - zmin) > NUMERICAL_CONVERGENCE_THRESHOLD_2 ) - { - z = (zmax + zmin) / 2; - pz = (z / PI) * hyp2f1a(beta, -std::pow( (z * R) / (mu * random_ensemble_kappa_per_degree_class[d1] * random_ensemble_kappa_per_degree_class[d3]), beta)) / p13; - if(pz > pc) - { - zmax = z; - } - else - { - zmin = z; - } - } - z13 = (zmax + zmin) / 2; - - - // - if(uniform_01(engine) < 0.5) - { - da = std::fabs(z12 + z13); - } - else - { - da = std::fabs(z12 - z13); - } - da = std::min(da, (2.0 * PI) - da); - if(da < NUMERICAL_ZERO) - { - p23 += 1; - } - else - { - p23 += 1.0 / (1.0 + std::pow((da * R) / (mu * random_ensemble_kappa_per_degree_class[d2] * random_ensemble_kappa_per_degree_class[d3]), beta)); - } - } - // Returns the value of the local clustering coefficient for this degree class. - return p23 / nb_points; -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -double embeddingS1_t::compute_pairwise_loglikelihood(int v1, double t1, int v2, double t2, bool neighbors) -{ - // Avoids to compute the pairwise loglikelihood of the vertex with itself. - if(v1 == v2) - { - return 0; - } - // Computes the angular separation. - double da = PI - std::fabs(PI - std::fabs(t1 - t2)); - // Computes the loglikelihood between vertives v1 and v2 according to whether they are neighbors or not. - if(neighbors) - { - return -1 * beta * std::log( (nb_vertices * da) / (2 * PI * mu * kappa[v1] * kappa[v2]) ); - } - else // not neighbors - { - return -1 * std::log( 1 + std::pow( (nb_vertices * da) / (2 * PI * mu * kappa[v1] * kappa[v2]), -beta) ); - } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::embed() -{ - // Gets current time. - time0 = time_since_epoch_in_seconds(); - time_started = std::time(NULL); - // Initialization. - initialize(); - // Gets current time. - time1 = time_since_epoch_in_seconds(); - if(REFINE_MODE) - { - // Loads the parameters from the .inf_coord file. - load_already_inferred_parameters(); - } - if(!REFINE_MODE) - { - // Pre-processing: infers the parameters used for the embedding. - infer_parameters(); - } - // Gets current time. - time2 = time_since_epoch_in_seconds(); - if(!REFINE_MODE) - { - // First phase: educated guess of the positions. - infer_initial_positions(); - } - // Gets current time. - time3 = time_since_epoch_in_seconds(); - if(MAXIMIZATION_MODE) - { - // Second phase: likelihood maximization. - // infer_optimal_positions(); - refine_positions(); - } - // Gets current time. - time4 = time_since_epoch_in_seconds(); - if(KAPPA_POST_INFERENCE_MODE) - { - // Post-processing: adjusts the values of kappa based on the inferred positions. - infer_kappas_given_beta_for_all_vertices(); - } - // Gets the current time. - time5 = time_since_epoch_in_seconds(); - time_ended = std::time(NULL); - // Saves the inferred coordinates in a file. - save_inferred_coordinates(); - if(VALIDATION_MODE) - { - save_inferred_theta_density(); - save_inferred_connection_probability(); - } - // Gets the current time. - time6 = time_since_epoch_in_seconds(); - if(CHARACTERIZATION_MODE) - { - save_inferred_ensemble_characterization(); - } - // Gets the current time. - time7 = time_since_epoch_in_seconds(); - time_ended = std::time(NULL); - // - finalize(); -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::extract_onion_decomposition(std::vector &coreness, std::vector &od_layer) -{ - // Builds two lists (std::vector, std::set) of the degree of the vertices. - std::vector DegreeVec(nb_vertices); - std::set > DegreeSet; - for(int v(0); v::iterator it1, end; - std::set< std::pair > LayerSet; - // std::set< std::pair > order_in_layer; - std::set< std::pair >::iterator m_it; - // std::set< std::pair >::iterator o_it, o_end; - while(DegreeSet.size() > 0) - { - // Populates the set containing the vertices belonging to the same layer. - m_it = DegreeSet.begin(); - d1 = m_it->first; - // Increases the layer id. - current_layer += 1; - // Sets the coreness and the layer the vertices with the same degree. - while(m_it->first == d1 && m_it != DegreeSet.end()) - { - // Sets the coreness and the layer. - v1 = m_it->second; - coreness[v1] = d1; - od_layer[v1] = current_layer; - // Looks at the next vertex. - ++m_it; - } - // Adds the vertices of the layer to the set. - LayerSet.insert(DegreeSet.begin(), m_it); - // Removes the vertices of the current layer. - DegreeSet.erase(DegreeSet.begin(), m_it); - // Modifies the "effective" degree of the neighbors of the vertices in the layer. - while(LayerSet.size() > 0) - { - // Gets information about the next vertex of the layer. - v1 = LayerSet.begin()->second; - // Reduces the "effective" degree of its neighbours. - it1 = adjacency_list[v1].begin(); - end = adjacency_list[v1].end(); - for(; it1!=end; ++it1) - { - // Identifies the neighbor. - v2 = *it1; - d2 = DegreeVec[v2]; - // Finds the neighbor in the list "effective" degrees. - m_it = DegreeSet.find(std::make_pair(d2, v2)); - if(m_it != DegreeSet.end()) - { - if(d2 > d1) - { - DegreeVec[v2] = d2 - 1; - DegreeSet.erase(m_it); - DegreeSet.insert(std::make_pair(d2 - 1, v2)); - } - } - } - // Removes the vertices from the LayerSet. - LayerSet.erase(LayerSet.begin()); - } - } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::finalize() -{ - // Resets the formating of std::clog. - std::clog << std::resetiosflags(std::ios::floatfield | std::ios::fixed | std::ios::showpoint); - // Writes the parameters used in the log for reproductibility. - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "Internal parameters and options" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "ALREADY_INFERRED_PARAMETERS_FILENAME " << ALREADY_INFERRED_PARAMETERS_FILENAME << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "BETA_ABS_MAX " << BETA_ABS_MAX << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "BETA_ABS_MIN " << BETA_ABS_MIN << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "CHARACTERIZATION_MODE " << (CHARACTERIZATION_MODE ? "true" : "false") << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "CHARACTERIZATION_NB_GRAPHS " << CHARACTERIZATION_NB_GRAPHS << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "CLEAN_RAW_OUTPUT_MODE " << (CLEAN_RAW_OUTPUT_MODE ? "true" : "false") << std::endl; } - // if(!QUIET_MODE) { std::clog << TAB << "CLOSE_ANGULAR_RANGE_FACTOR " << CLOSE_ANGULAR_RANGE_FACTOR << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_BETA " << (CUSTOM_BETA ? "true" : "false") << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_CHARACTERIZATION_NB_GRAPHS " << (CUSTOM_CHARACTERIZATION_NB_GRAPHS ? "true" : "false") << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_INFERRED_COORDINATES " << (CUSTOM_INFERRED_COORDINATES ? "true" : "false") << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_OUTPUT_ROOTNAME_MODE " << (CUSTOM_OUTPUT_ROOTNAME_MODE ? "true" : "false") << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_SEED " << (CUSTOM_SEED ? "true" : "false") << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "EDGELIST_FILENAME: " << EDGELIST_FILENAME << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "EXP_CLUST_NB_INTEGRATION_MC_STEPS " << EXP_CLUST_NB_INTEGRATION_MC_STEPS << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "EXP_DIST_NB_INTEGRATION_STEPS " << EXP_DIST_NB_INTEGRATION_STEPS << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "KAPPA_MAX_NB_ITER_CONV " << KAPPA_MAX_NB_ITER_CONV << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "KAPPA_POST_INFERENCE_MODE " << (KAPPA_POST_INFERENCE_MODE ? "true" : "false") << std::endl; } - // if(!QUIET_MODE) { std::clog << TAB << "LIMIT_FOR_CONVERGENCE_CRITERION " << LIMIT_FOR_CONVERGENCE_CRITERION << std::endl; } - // if(!QUIET_MODE) { std::clog << TAB << "MAX_NB_ITER_MAXIMIZATION " << MAX_NB_ITER_MAXIMIZATION << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "MAXIMIZATION_MODE " << (MAXIMIZATION_MODE ? "true" : "false") << std::endl; } - // if(!QUIET_MODE) { std::clog << TAB << "MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD " << MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD << std::endl; } - // if(!QUIET_MODE) { std::clog << TAB << "MINIMAL_ANGULAR_RESOLUTION " << MINIMAL_ANGULAR_RESOLUTION << std::endl; } - // if(!QUIET_MODE) { std::clog << TAB << "NB_VERTICES_IN_CORE " << NB_VERTICES_IN_CORE << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "MIN_NB_ANGLES_TO_TRY " << MIN_NB_ANGLES_TO_TRY << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_1 " << NUMERICAL_CONVERGENCE_THRESHOLD_1 << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_2 " << NUMERICAL_CONVERGENCE_THRESHOLD_2 << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_3 " << NUMERICAL_CONVERGENCE_THRESHOLD_3 << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "NUMERICAL_ZERO " << NUMERICAL_ZERO << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "QUIET_MODE " << (QUIET_MODE ? "true" : "false") << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "REFINE_MODE " << (REFINE_MODE ? "true" : "false") << std::endl; } - // if(!QUIET_MODE) { std::clog << TAB << "REFINED_MAX_STEP_LENGTH_DIVISOR " << REFINED_MAX_STEP_LENGTH_DIVISOR << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "ROOTNAME_OUTPUT: " << ROOTNAME_OUTPUT << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "SEED " << SEED << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "VALIDATION_MODE " << (VALIDATION_MODE ? "true" : "false") << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "VERBOSE_MODE " << (VERBOSE_MODE ? "true" : "false") << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "VERSION " << VERSION << std::endl; } - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "Ended on: " << format_time(time_ended) << std::endl; } - if(!QUIET_MODE) { std::clog << "Elapsed CPU time (embedding): " << std::setw(10) << std::fixed << time5 - time0 << " seconds" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "initialization: " << std::setw(10) << std::fixed << time1 - time0 << " seconds" << std::endl; } - if(!REFINE_MODE) - if(!QUIET_MODE) { std::clog << TAB << "parameters inference: " << std::setw(10) << std::fixed << time2 - time1 << " seconds" << std::endl; } - if(!REFINE_MODE) - if(!QUIET_MODE) { std::clog << TAB << "initial positions: " << std::setw(10) << std::fixed << time3 - time2 << " seconds" << std::endl; } - if(REFINE_MODE) - if(!QUIET_MODE) { std::clog << TAB << "loading previous positions: " << std::setw(10) << std::fixed << time3 - time1 << " seconds" << std::endl; } - if(MAXIMIZATION_MODE) - if(!QUIET_MODE) { std::clog << TAB << "refining positions: " << std::setw(10) << std::fixed << time4 - time3 << " seconds" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "adjusting kappas: " << std::setw(10) << std::fixed << time5 - time4 << " seconds" << std::endl; } - if(VALIDATION_MODE || CHARACTERIZATION_MODE) - if(!QUIET_MODE) { std::clog << "Elapsed CPU time (validation): " << std::setw(10) << std::fixed << time7 - time5 << " seconds" << std::endl; } - if(VALIDATION_MODE) - if(!QUIET_MODE) { std::clog << TAB << "validating embedding: " << std::setw(10) << std::fixed << time6 - time5 << " seconds" << std::endl; } - if(CHARACTERIZATION_MODE) - if(!QUIET_MODE) { std::clog << TAB << "characterizing ensemble: " << std::setw(10) << std::fixed << time7 - time6 << " seconds" << std::endl; } - if(!QUIET_MODE) { std::clog << "===========================================================================================" << std::endl; } - // Puts the streams back into place (to avoid a segmentation fault when exiting program). - if(!QUIET_MODE) - { - if(!VERBOSE_MODE) - { - logfile.close(); - // Reset the rdbuf of clog. - std::clog.rdbuf(old_rdbuf); - } - } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::find_initial_ordering(std::vector &ordering, std::vector &raw_theta) -{ - if(!QUIET_MODE) { std::clog << std::endl << TAB << "Building the weights matrix..."; } - // Initializes the sparse matrix. - Eigen::SparseMatrix L(nb_vertices_degree_gt_one, nb_vertices_degree_gt_one); - L.reserve(Eigen::VectorXi::Constant(nb_vertices_degree_gt_one, 3)); - // ASSUMES THAT THERE IS ONLY ONE CONNECTED COMPONENT. - - // Sets contiguous IDs excluding vertices with degree 1. - std::vector newID(nb_vertices, -1); - int n(0); - for(int v(0); v 1) - { - newID[v] = n; - ++n; - } - } - if(n != nb_vertices_degree_gt_one) - std::cout << "There is something wrong here." << std::endl; - - // First, fills the matrix with the expected distance between connected vertices according to S1. - double k1, k2, expected_distance, val, t(0), norm(0); - std::set::iterator it, end; - for(int v1(0), v2, d1, d2, n1, n2; v1 PI) - { - // Just a verification. - std::cerr << "Warning. Expected angular distance out of range." << std::endl; - std::terminate(); - } - // Transforms the arc length distance into Euclidean distances (unit circle). - expected_distance = 2 * std::sin(expected_distance / 2); - // Fills the matrix. - L.insert(n1, n2) = expected_distance; - L.insert(n2, n1) = expected_distance; - // Scaling factor of the weights (see below). - t += 2 * expected_distance * expected_distance; - norm += 2; - } - } - } - } - } - t /= norm; - // Second, takes the appropriate scaled exponential of each entry and computes the "strengths". - double value; - std::vector strength(nb_vertices_degree_gt_one, 0); - for(int k(0), kk(L.outerSize()), v1, v2; k::InnerIterator it(L, k); it; ++it) - { - v1 = it.row(); - v2 = it.col(); - value = it.value(); - value = std::exp(-1 * value * value / t); - L.coeffRef(v1, v2) = value; - strength[v1] += value; - } - } - // Third, multiply by the left with the inverse matrix of the strengths to tranform the - // generalized eigenvalue problem into a "regular" eigenvalue problem. - for(int k(0), kk(L.outerSize()), v1, v2; k::InnerIterator it(L, k); it; ++it) - { - v1 = it.row(); - v2 = it.col(); - value = it.value(); - L.coeffRef(v1, v2) = -1 * value / strength[v1]; - } - } - // Fourth, fills the diagonal. - for(int v1(0); v1 op(L); - // Containers for the eigenvectors. - Eigen::MatrixXcd evectors; - // Convergence parameter. - int ncv = 7; - // Initializes and computes. - // if(!QUIET_MODE) { std::clog << std::endl << TAB << "Using Spectra parameter ncv = " << ncv << " to compute eigenvectors...";} - // eigs.init(); - // int nconv = eigs.compute(); - // Flag indicating whether another iteration is required. - if(!QUIET_MODE) { std::clog << std::endl; } - bool keep_going = true; - while(keep_going) - { - // // Checks for errors. - // if(eigs.info() != Spectra::SUCCESSFUL) - // { - // if(eigs.info() == Spectra::NOT_COMPUTED) - // std::cerr << "NOT_COMPUTED" << std::endl; - // if(eigs.info() == Spectra::NOT_CONVERGING) - // std::cerr << "NOT_CONVERGING" << std::endl; - // if(eigs.info() == Spectra::NUMERICAL_ISSUE) - // std::cerr << "NUMERICAL_ISSUE" << std::endl; - // std::terminate(); - // } - // Constructs eigen solver object. - Spectra::GenEigsSolver< double, Spectra::SMALLEST_MAGN, Spectra::SparseGenMatProd > eigs(&op, 3, ncv); - // Initializes and computes. - if(!QUIET_MODE) { std::clog << TAB << "Computing eigenvectors using Spectra parameter ncv = " << ncv << "..."; } - eigs.init(); - int nconv = eigs.compute(); - // Retrieves the eigenvectors. - evectors = eigs.eigenvectors(); - - // Checks whether another iteration is required. - if(eigs.info() != Spectra::SUCCESSFUL) - { - // Checks that the convergence parameter has not reached its maximal value. - if(ncv == nb_vertices) - { - std::cerr << std::endl << "The algorithm computing the eigenvectors (Spectra library) cannot converge at all... Exiting." << std::endl << std::endl; - std::terminate(); - } - if(!QUIET_MODE) { std::clog << " Convergence not reached." << std::endl; } - // Increases the convergence parameter. - ncv = std::pow(ncv, 1.5); - if(ncv > nb_vertices) - { - ncv = nb_vertices; - } - } - else - { - keep_going = false; - } - } - if(!QUIET_MODE) { std::clog << " Convergence reached." << std::endl; } - - /*TEST*/// Initializes the container for the raw angular positions. - /*TEST*/raw_theta.clear(); - /*TEST*/raw_theta.resize(nb_vertices); - // Orders the vertices. - double angle; - std::set< std::pair > ordering_set; - ordering_set.clear(); - for(int v1(0), n1; v1 list_neigh_degree_one; - std::set< std::pair >::iterator it2 = ordering_set.begin(); - std::set< std::pair >::iterator end2 = ordering_set.end(); - for(int v1; it2!=end2; ++it2) - { - // Gets the identity of the vertex. - v1 = it2->second; - // Counts the number of neighbors with degree 1 - list_neigh_degree_one.clear(); - list_neigh_degree_one.reserve(degree[v1]); - it = adjacency_list[v1].begin(); - end = adjacency_list[v1].end(); - for(int v2; it!=end; ++it) - { - v2 = *it; - if(degree[v2] == 1) - { - list_neigh_degree_one.push_back(v2); - } - } - // Adds the neighbors of degree 1 around the vertex in the ordering. - for(int v(0), vv(list_neigh_degree_one.size() / 2); vtm_year + 1900; - int month = aTime->tm_mon + 1; - int day = aTime->tm_mday; - int hours = aTime->tm_hour; - int minutes = aTime->tm_min; - // Format the string. - std::string the_time = std::to_string(year) + "/"; - if(month < 10) - the_time += "0"; - the_time += std::to_string(month) + "/"; - if(day < 10) - the_time += "0"; - the_time += std::to_string(day) + " " + std::to_string(hours) + ":"; - if(minutes < 10) - the_time += "0"; - the_time += std::to_string(minutes) + " UTC"; - // Returns the date/time. - return the_time; -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::generate_simulated_adjacency_list() -{ - // Initializes the container. - simulated_adjacency_list.clear(); - simulated_adjacency_list.resize(nb_vertices); - // Generates the adjacency list. - double kappa1, theta1, dtheta, prob; - double prefactor = nb_vertices / (2 * PI * mu); - for(int v1(0); v1 ordering; - std::vector raw_theta; - find_initial_ordering(ordering, raw_theta); - - if(ordering.size() != nb_vertices) - std::cout << TAB << "WARNING: All degree-one vertices have not all been reinserted. Does the original edgelist have more than one connected component." << std::endl; - - // Initializes the container for the angular positions. - theta.clear(); - theta.resize(nb_vertices); - - // Spaces the vertices based on the expected angular distance between (non-)neighbors. - int v0, v1, n(0); - double factor, int1, int2, b, tmp; - double norm = 0; - double possible_dtheta1, possible_dtheta2; - double dx = PI / EXP_DIST_NB_INTEGRATION_STEPS; - double prefactor = nb_vertices / (2 * PI * mu); - double avg_gap = 2 * PI / nb_vertices; - std::vector::iterator it = ordering.begin(); - std::vector::iterator end = ordering.end(); - // Identifies the first vertex (takes care of the periodic condition of the circle). - v0 = *ordering.rbegin(); - for(; it!=end; ++it, ++n) - { - // Identifies the second vertex. - v1 = *it; - // Numerical integration of the expected angular distance between two consecutive vertices. - int1 = 0; - int2 = 0; - tmp = 0; - factor = prefactor / ( random_ensemble_kappa_per_degree_class[degree[v0]] * random_ensemble_kappa_per_degree_class[degree[v1]] ); - // Adjusts the sign of beta according to whether the vertices are connected or not. - b = beta; - if(adjacency_list[v0].find(v1) == adjacency_list[v0].end()) - { - b = -beta; // not connected - } - // Lower bound of the integral (no contribution if not connected). - if(b > 0) - { - int2 += 0.5; - } - // Upper bound of the integral. - tmp = std::exp(-PI / avg_gap) / ( 1 + std::pow(factor * PI, b) ); - int1 += PI * tmp / 2; - int2 += tmp / 2; - // In-between points. - for(double da = dx; da < PI; da += dx) - { - tmp = std::exp(-da / avg_gap) / ( 1 + std::pow(factor * da, b) ); - int1 += da * tmp; - int2 += tmp; - } - // Uses the value of the expected gap to position the vertices. - /*TEST*/possible_dtheta1 = int1 / int2; - /*TEST*/possible_dtheta2 = PI - std::fabs(PI - std::fabs(raw_theta[v1] - raw_theta[v0])); - if(possible_dtheta1 > possible_dtheta2) - { - norm += possible_dtheta1; - } - else - { - norm += possible_dtheta2; - } - // norm += int1 / int2; - theta[v1] = norm; - // Switches the identities of the vertices prior to the next gap. - - v0 = v1; - } - if(!QUIET_MODE) { std::clog << std::endl << TAB << "Sum of the angular positions (before adjustment): " << norm << std::endl; } - // Rescales the angles to limit their span in the [0, 2 PI) range. - norm /= 2 * PI; - for(int v(0); v NUMERICAL_CONVERGENCE_THRESHOLD_3) - { - keep_going = true; - continue; - } - } - // Modifies the value of the kappas prior to the next iteration, if required. - if(keep_going) - { - for(int v(0); v= KAPPA_MAX_NB_ITER_CONV) - { - if(!QUIET_MODE) { std::clog << TAB << "WARNING: maximum number of iterations reached before convergence. This limit can be" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << " adjusted by setting the parameters KAPPA_MAX_NB_ITER_CONV to desired value." << std::endl; } - } - else - { - if(!QUIET_MODE) { std::clog << TAB << "Convergence reached after " << cnt << " iterations." << std::endl; } - } - if(!QUIET_MODE) { std::clog << " ...............................done." << std::endl; } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::infer_kappas_given_beta_for_degree_class() -{ - // Variable. - double prob_conn; - // Parameters. - mu = beta * std::sin(PI / beta) / (2.0 * PI * average_degree); - // Iterators. - std::set::iterator it1, it2, end; - // Initializes the kappas for each degree class. - it1 = degree_class.begin(); - end = degree_class.end(); - for(; it1!=end; ++it1) - { - random_ensemble_kappa_per_degree_class[*it1] = *it1; - } - - // Finds the values of kappa generating the degree classes, given the parameters. - int cnt = 0; - bool keep_going = true; - while( keep_going && (cnt < KAPPA_MAX_NB_ITER_CONV) ) - { - // Initializes the expected degree of each degree class. - it1 = degree_class.begin(); - end = degree_class.end(); - for(; it1!=end; ++it1) - { - random_ensemble_expected_degree_per_degree_class[*it1] = 0; - } - // Computes the expected degrees given the actual kappas. - it1 = degree_class.begin(); - end = degree_class.end(); - for(; it1!=end; ++it1) - { - it2 = it1; - prob_conn = hyp2f1a(beta, -std::pow(nb_vertices / (2.0 * mu * random_ensemble_kappa_per_degree_class[*it1] * random_ensemble_kappa_per_degree_class[*it2]), beta)); - random_ensemble_expected_degree_per_degree_class[*it1] += prob_conn * (degree2vertices[*it2].size() - 1); - for(++it2; it2!=end; ++it2) - { - prob_conn = hyp2f1a(beta, -std::pow(nb_vertices / (2.0 * mu * random_ensemble_kappa_per_degree_class[*it1] * random_ensemble_kappa_per_degree_class[*it2]), beta)); - random_ensemble_expected_degree_per_degree_class[*it1] += prob_conn * degree2vertices[*it2].size(); - random_ensemble_expected_degree_per_degree_class[*it2] += prob_conn * degree2vertices[*it1].size(); - } - } - // Verifies convergence. - keep_going = false; - it1 = degree_class.begin(); - end = degree_class.end(); - for(; it1!=end; ++it1) - { - if(std::fabs(random_ensemble_expected_degree_per_degree_class[*it1] - *it1) > NUMERICAL_CONVERGENCE_THRESHOLD_1) - { - keep_going = true; - } - } - // Modifies the value of the kappas prior to the next iteration, if required. - if(keep_going) - { - it1 = degree_class.begin(); - end = degree_class.end(); - for(; it1!=end; ++it1) - { - random_ensemble_kappa_per_degree_class[*it1] += (*it1 - random_ensemble_expected_degree_per_degree_class[*it1]) * uniform_01(engine); - random_ensemble_kappa_per_degree_class[*it1] = std::fabs(random_ensemble_kappa_per_degree_class[*it1]); - } - } - ++cnt; - } - if(cnt >= KAPPA_MAX_NB_ITER_CONV) - { - if(!QUIET_MODE) { std::clog << std::endl; } - // if(!QUIET_MODE) { std::clog << "WARNING: maximum number of iterations reached before convergence" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "WARNING: maximum number of iterations reached before convergence. This limit can be" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << " adjusted by setting the parameters KAPPA_MAX_NB_ITER_CONV to desired value." << std::endl; } - if(!QUIET_MODE) { std::clog << TAB; } - if(!QUIET_MODE) { std::clog << std::fixed << std::setw(11) << " " << " "; } - } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::infer_parameters() -{ - if(!QUIET_MODE) { std::clog << "Inferring parameters..."; } - if(!CUSTOM_BETA) - { - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << TAB; } - if(!QUIET_MODE) { std::clog << std::fixed << std::setw(11) << "beta" << " "; } - if(!QUIET_MODE) { std::clog << std::fixed << std::setw(20) << "avg. clustering" << " "; } - if(!QUIET_MODE) { std::clog << std::endl; } - // Sets initial value to beta. - beta = 2 + uniform_01(engine); - // Iterates until convergence is reached. - double beta_max = -1; - double beta_min = 1; - random_ensemble_average_clustering = 10; // dummy value to enter the while loop. - while( true ) - { - if(!QUIET_MODE) { std::clog << TAB; } - if(!QUIET_MODE) { std::clog << std::fixed << std::setw(11) << beta << " "; } - if(!QUIET_MODE) { std::clog.flush(); } - // Infers the values of kappa. - infer_kappas_given_beta_for_degree_class(); - // Computes the cumulative distribution used in the MC integration. - build_cumul_dist_for_mc_integration(); - // Computes the ensemble clustering. - compute_random_ensemble_clustering(); - if(!QUIET_MODE) { std::clog << std::fixed << std::setw(20) << random_ensemble_average_clustering << " "; } - if(!QUIET_MODE) { std::clog << std::endl; } - - // Checks if the expected clustering is close enough. - if( std::fabs(random_ensemble_average_clustering - average_clustering) < NUMERICAL_CONVERGENCE_THRESHOLD_1 ) - { - break; - } - - // Modifies the bounds on beta if another iteration is required. - if(random_ensemble_average_clustering > average_clustering) - { - beta_max = beta; - beta = (beta_max + beta_min) / 2; - if(beta < BETA_ABS_MIN) - { - if(!QUIET_MODE) { std::clog << "WARNING: value too close to 1, using beta = " << std::fixed << std::setw(11) << beta << "."; } - if(!QUIET_MODE) { std::clog << std::endl; } - break; - } - } - else - { - beta_min = beta; - if(beta_max == -1) - { - beta *= 1.5; - } - else - { - beta = (beta_max + beta_min) / 2; - } - } - if(beta > BETA_ABS_MAX) - { - if(!QUIET_MODE) { std::clog << "WARNING: value too high, using beta = " << std::fixed << std::setw(11) << beta << "."; } - if(!QUIET_MODE) { std::clog << std::endl; } - break; - } - } - } - else - { - // Infers the values of kappa. - infer_kappas_given_beta_for_degree_class(); - // Computes the cumulative distribution used in the MC integration. - build_cumul_dist_for_mc_integration(); - // Computes the ensemble clustering. - compute_random_ensemble_clustering(); - } - // Computes the ensemble average degree. - compute_random_ensemble_average_degree(); - // Sets the kappas. - kappa.clear(); - kappa.resize(nb_vertices); - for(int v(0); vfirst << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Maximum degree: " << (--random_ensemble_expected_degree_per_degree_class.end())->first << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Average clustering: " << random_ensemble_average_clustering << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Parameters" << std::endl; } - if(!CUSTOM_BETA) - { - if(!QUIET_MODE) { std::clog << TAB << " - beta: " << beta << std::endl; } - } - else - { - if(!QUIET_MODE) { std::clog << TAB << " - beta: " << beta << " (custom)" << std::endl; } - } - if(!QUIET_MODE) { std::clog << TAB << " - mu: " << mu << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << " - radius_S1 (R): " << nb_vertices / (2 * PI) << std::endl; } - if(!QUIET_MODE) { std::clog << std::endl; } - - // Cleans containers that are no longer useful. - cumul_prob_kgkp.clear(); - degree2vertices.clear(); - random_ensemble_expected_degree_per_degree_class.clear(); - // random_ensemble_kappa_per_degree_class.clear(); -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::initialize() -{ - // Sets the default rootname for output files. - if(CUSTOM_OUTPUT_ROOTNAME_MODE == false) - { - size_t lastdot = EDGELIST_FILENAME.find_last_of("."); - if(lastdot == std::string::npos) - { - ROOTNAME_OUTPUT = EDGELIST_FILENAME; - } - ROOTNAME_OUTPUT = EDGELIST_FILENAME.substr(0, lastdot); - } - // // Gets current time. - // time0 = std::time(NULL); - // Initializes the random number generator. - if(!CUSTOM_SEED) - { - SEED = std::time(NULL); - } - engine.seed(SEED); - // Change the stream std::clog to a file. - if(!QUIET_MODE) - { - if(!VERBOSE_MODE) - { - logfile.open(ROOTNAME_OUTPUT + ".inf_log"); - // Get the rdbuf of clog. - // We need it to reset the value before exiting. - old_rdbuf = std::clog.rdbuf(); - // Set the rdbuf of clog. - std::clog.rdbuf(logfile.rdbuf()); - } - } - // Outputs options and parameters on screen. - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "===========================================================================================" << std::endl; } - if(!QUIET_MODE) { std::clog << "Mercador: accurate embeddings of graphs in the S1 space" << std::endl; } - if(!QUIET_MODE) { std::clog << "version: " << VERSION << std::endl; } - if(!QUIET_MODE) { std::clog << "started on: " << format_time(time_started) << std::endl; } - if(!QUIET_MODE) { std::clog << "edgelist filename: " << EDGELIST_FILENAME << std::endl; } - if(REFINE_MODE) - { - if(!QUIET_MODE) { std::clog << "inferred positions filename: " << ALREADY_INFERRED_PARAMETERS_FILENAME << std::endl; } - } - if(!QUIET_MODE) { std::clog << "seed: " << SEED << std::endl; } - - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "Loading edgelist..."; } - load_edgelist(); - if(!QUIET_MODE) { std::clog << "...................................................................done." << std::endl; } - - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "Checking number of connected components..."; } - check_connected_components(); - if(!QUIET_MODE) { std::clog << "............................................done." << std::endl; } - - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "Analyzing degrees..."; } - analyze_degrees(); - if(!QUIET_MODE) { std::clog << "..................................................................done." << std::endl; } - - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "Computing local clustering..."; } - compute_clustering(); - if(!QUIET_MODE) { std::clog << ".........................................................done." << std::endl; } - - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "Ordering vertices..."; } - order_vertices(); - if(!QUIET_MODE) { std::clog << "..................................................................done." << std::endl; } - if(!QUIET_MODE) { std::clog << std::endl; } - - // Sets the decimal precision of the log. - std::clog.precision(4); - - // Sets the width of the columns in the output files. - width_values = 15; - width_names = 14; - for(int v(0), l; v width_names) - { - width_names = l; - } - } - width_names += 1; - - if(!QUIET_MODE) { std::clog << "Properties of the graph" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Nb vertices: " << nb_vertices << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Nb edges: " << nb_edges << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Average degree: " << average_degree << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Minimum degree: " << *(degree_class.begin()) << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Maximum degree: " << *(--degree_class.end()) << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Nb of degree class: " << degree_class.size() << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "Average clustering: " << average_clustering << std::endl; } - if(!QUIET_MODE) { std::clog << std::endl; } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::load_already_inferred_parameters() -{ - // Stream object. - std::stringstream one_line; - // String objects. - std::string full_line, name1_str, name2_str, name3_str; - // Resets the containers. - kappa.clear(); - kappa.resize(nb_vertices); - theta.clear(); - theta.resize(nb_vertices); - // Opens the stream and terminates if the operation did not succeed. - std::fstream hidden_variables_file(ALREADY_INFERRED_PARAMETERS_FILENAME.c_str(), std::fstream::in); - if( !hidden_variables_file.is_open() ) - { - std::cerr << "Could not open file: " << ALREADY_INFERRED_PARAMETERS_FILENAME << "." << std::endl; - std::terminate(); - } - // Extracts the beta and mu parameters. - // Ignores the first 9 lines of the file. - for(int l(0); l<8; ++l) - { - std::getline(hidden_variables_file, full_line); - } - // Gets the 10th lines containing the value of beta. - std::getline(hidden_variables_file, full_line); - hidden_variables_file >> std::ws; - one_line.str(full_line); - one_line >> std::ws; - one_line >> name1_str >> std::ws; - one_line >> name1_str >> std::ws; - one_line >> name1_str >> std::ws; - one_line >> name1_str >> std::ws; - beta = std::stod(name1_str); - one_line.clear(); - // Gets the 11th lines containing the value of mu. - std::getline(hidden_variables_file, full_line); - hidden_variables_file >> std::ws; - one_line.str(full_line); - one_line >> std::ws; - one_line >> name1_str >> std::ws; - one_line >> name1_str >> std::ws; - one_line >> name1_str >> std::ws; - one_line >> name1_str >> std::ws; - mu = std::stod(name1_str); - one_line.clear(); - // Reads the hidden variables file line by line. - while( !hidden_variables_file.eof() ) - { - // Reads a line of the file. - std::getline(hidden_variables_file, full_line); - hidden_variables_file >> std::ws; - one_line.str(full_line); - one_line >> std::ws; - one_line >> name1_str >> std::ws; - // Skips lines of comment. - if(name1_str == "#") - { - one_line.clear(); - continue; - } - one_line >> name2_str >> std::ws; - kappa[ Name2Num[name1_str] ] = std::stod(name2_str); - one_line >> name3_str >> std::ws; - theta[ Name2Num[name1_str] ] = std::stod(name3_str); - one_line.clear(); - } - // Closes the stream. - hidden_variables_file.close(); - // Clears unused objects. - Name2Num.clear(); -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::load_edgelist() -{ - // Stream objects. - std::ifstream edgelist_file; - std::stringstream one_line; - // Variables. - int v1, v2; - // String objects. - std::string full_line, name1_str, name2_str; - // Iterator objects. - std::map< std::string, int >::iterator name_it; - // Resets the number of vertices and of edges. - nb_vertices = 0; - nb_edges = 0; - // Resets the containers. - adjacency_list.clear(); - // Opens the stream and terminates if the operation did not succeed. - edgelist_file.open(EDGELIST_FILENAME.c_str(), std::ios_base::in); - if( !edgelist_file.is_open() ) - { - std::cerr << "Could not open file: " << EDGELIST_FILENAME << "." << std::endl; - std::terminate(); - } - else - { - // Reads the edgelist file line by line. - while( !edgelist_file.eof() ) - { - // Reads a line of the file. - std::getline(edgelist_file, full_line); edgelist_file >> std::ws; - one_line.str(full_line); one_line >> std::ws; - one_line >> name1_str >> std::ws; - // Skips lines of comment. - if(name1_str == "#") - { - one_line.clear(); - continue; - } - one_line >> name2_str >> std::ws; - one_line.clear(); - // Does not consider self-loops. - if(name1_str != name2_str) - { - // Is name1 new? - name_it = Name2Num.find(name1_str); - if( name_it == Name2Num.end() ) - { - // New vertex. - v1 = nb_vertices; - Name2Num[name1_str] = v1; - Num2Name.push_back(name1_str); - adjacency_list.push_back(std::set()); - ++nb_vertices; - } - else - { - // Known vertex. - v1 = name_it->second; - } - // Is name2 new? - name_it = Name2Num.find(name2_str); - if( name_it == Name2Num.end() ) - { - // New vertex. - v2 = nb_vertices; - Name2Num[name2_str] = v2; - Num2Name.push_back(name2_str); - adjacency_list.push_back(std::set()); - ++nb_vertices; - } - else - { - // Known vertex. - v2 = name_it->second; - } - // Adds the edge to the adjacency list (multiedges are ignored due to std::set). - std::pair< std::set::iterator, bool > add1 = adjacency_list[v1].insert(v2); - std::pair< std::set::iterator, bool > add2 = adjacency_list[v2].insert(v1); - if(add1.second && add2.second) // Both bool should always agree. - { - ++nb_edges; - } - } - } - } - // Closes the stream. - edgelist_file.close(); - if(!REFINE_MODE) - { - // Clears unused objects. - Name2Num.clear(); - } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::order_vertices() -{ - // Containers related to the results of the onion decomposition. - std::vector coreness(nb_vertices); - std::vector od_layer(nb_vertices); - // Extracts the onion decomposition. - extract_onion_decomposition(coreness, od_layer); - // Orders the vertices based on their layer. - std::set< std::pair > > layer_set; - for(int v(0); v > >::reverse_iterator it = layer_set.rbegin(); - std::set< std::pair > >::reverse_iterator end = layer_set.rend(); - for(int v(0); it!=end; ++it, ++v) - { - ordered_list_of_vertices[v] = it->second.second; - } - layer_set.clear(); -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -int embeddingS1_t::refine_angle(int v1) -{ - // Variables. - int has_moved = 0; - double tmp_angle; - double tmp_loglikelihood; - double best_angle = theta[v1]; - // Iterators. - std::set::iterator it2, end; - // Computes the current loglikelihood. - double previous_loglikelihood = 0; - for(int v2(0); v2 (2 * PI)) - average_theta = average_theta - (2 * PI); - while(average_theta < 0) - average_theta = average_theta + (2 * PI); - - // Finds the largest angular distance between the neighbor and the average position. - double max_angle = MIN_TWO_SIGMAS_NORMAL_DIST; - it2 = adjacency_list[v1].begin(); - for(; it2!=end; ++it2) - { - da = PI - std::fabs(PI - std::fabs(average_theta - theta[*it2])); - if(da > max_angle) - { - max_angle = da; - } - } - max_angle /= 2; - - // Considers various wisely chosen new angular positions and keeps the best. - int _nb_new_angles_to_try = MIN_NB_ANGLES_TO_TRY * std::max(1.0, std::log(nb_vertices)); - for(int e(0); e<_nb_new_angles_to_try; ++e) - { - // Gets the angle in the standard range. - tmp_angle = (normal_01(engine) * max_angle) + average_theta; - while(tmp_angle > (2 * PI)) - tmp_angle = tmp_angle - (2 * PI); - while(tmp_angle < 0) - tmp_angle = tmp_angle + (2 * PI); - - // Computes the local loglikelihood. - tmp_loglikelihood = 0; - for(int v2(0); v2 best_loglikelihood) - { - best_loglikelihood = tmp_loglikelihood; - best_angle = tmp_angle; - has_moved = 1; - } - } - - // Registers the best position found. - theta[v1] = best_angle; - // Returns 1 if the vertex changed position, and 0 otherwise. - return has_moved; -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::refine_positions() -{ - if(!QUIET_MODE) { std::clog << "Refining the positions..."; } - if(!QUIET_MODE) { std::clog << std::endl; } - - // // Imposes a global random shift on the angular positions. - // double theta_shift = 2 * PI * uniform_01(engine); - // // double theta_shift = PI / 2; - // for(int i(0); i= (2 * PI)) - // theta[i] = theta[i] - (2 * PI); - // while(theta[i] < 0) - // theta[i] = theta[i] + (2 * PI); - // } - - double start_time, stop_time; - std::string vertices_range; - int delta_nb_vertices = nb_vertices / 19.999999; - if(delta_nb_vertices < 1) { delta_nb_vertices = 1; } - int width = 2 * (std::log10(nb_vertices) + 1) + 6; - for(int v_i(0), v_f(0), v_m, n_v; v_f nb_vertices) ? nb_vertices : v_f; - n_v = v_f - v_i; - start_time = time_since_epoch_in_seconds(); - if(!QUIET_MODE) { vertices_range = "[" + std::to_string(v_i+1) + "," + std::to_string(v_f) + "]..."; } - if(!QUIET_MODE) { std::clog << TAB << "...of vertices " << std::setw(width) << vertices_range; } - for(v_m = 0; v_iNB_VERTICES_IN_CORE) ? std::to_string(NB_VERTICES_IN_CORE) : std::to_string(nb_vertices) ) + " vertices, iteration #" + std::to_string(j+1) + ")"; std::clog.clear(); } - // for(int i(0); (i NB_VERTICES_IN_CORE) - // { - // if(!QUIET_MODE) { std::clog << TAB << "refining the positions of the remaining vertices (" + std::to_string(nb_vertices - NB_VERTICES_IN_CORE) + " vertices)"; std::clog.clear(); } - // for(int i(NB_VERTICES_IN_CORE); i bins; - std::map::iterator it; - int bound = 20; - int cnt = 0; - double dt = 0.05; - for(double t(-bound), tt(bound + 0.000001); t n(bins.size(), 0); - std::vector p(bins.size(), 0); - std::vector x(bins.size(), 0); - // Computes the connection probability for every pair of vertices. - double k1; - double t1; - double da; - double dist; - for(int v1(0), i; v1second; - n[i] += 1; - x[i] += dist; - if(adjacency_list[v1].find(v2) != adjacency_list[v1].end()) - { - p[i] += 1; - } - } - } - // Writes the connection probability into a file. - std::string pconn_filename = ROOTNAME_OUTPUT + ".inf_pconn"; - std::fstream pconn_file(pconn_filename.c_str(), std::fstream::out); - if( !pconn_file.is_open() ) - { - std::cerr << "Could not open file: " << pconn_filename << "." << std::endl; - std::terminate(); - } - pconn_file << "#"; - pconn_file << std::setw(width_values - 1) << "RescaledDist" << " "; - pconn_file << std::setw(width_values) << "InfConnProb" << " "; - pconn_file << std::setw(width_values) << "ThConnProb" << " "; - pconn_file << std::endl; - for(int i(0), ii(n.size()); i 0) - { - pconn_file << std::setw(width_values) << x[i] / n[i] << " "; - pconn_file << std::setw(width_values) << p[i] / n[i] << " "; - pconn_file << std::setw(width_values) << 1 / (1 + std::pow(x[i] / n[i], beta) ) << " "; - pconn_file << std::endl; - } - } - // Closes the stream. - pconn_file.close(); - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "=> Inferred connection probability saved to " << ROOTNAME_OUTPUT + ".inf_pconn" << std::endl; } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::save_inferred_coordinates() -{ - // Finds the minimal and maximal values of kappa. - double kappa_min = *std::min_element(kappa.begin(), kappa.end()); - double kappa_max = *std::max_element(kappa.begin(), kappa.end()); - // Computes the hyperbolic radius (adjusts it in case some vertices have a negative radial position). - double hyp_radius = 2 * std::log( nb_vertices / (PI * mu * kappa_min * kappa_min) ); - double min_radial_position = hyp_radius - 2 * std::log( kappa_min / kappa_max ); - bool warning = false; - if(min_radial_position < 0) - { - hyp_radius += std::fabs(min_radial_position); - warning = true; - } - // Sets the name of the file to write the hidden variables into. - std::string coordinates_filename = ROOTNAME_OUTPUT + ".inf_coord"; - // // Gets the current time. - // time1 = std::time(NULL); - // Opens the stream and terminates if the operation did not succeed. - std::fstream coordinates_file(coordinates_filename.c_str(), std::fstream::out); - if( !coordinates_file.is_open() ) - { - std::cerr << "Could not open file: " << coordinates_filename << "." << std::endl; - std::terminate(); - } - // Writes the header. - coordinates_file << "# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=" << std::endl; - coordinates_file << "# Embedding started at: " << format_time(time_started) << std::endl; - coordinates_file << "# Ended at: " << format_time(time_ended) << std::endl; - coordinates_file << "# Elapsed CPU time: " << time5 - time0 << " seconds" << std::endl; - coordinates_file << "# Edgelist file: " << EDGELIST_FILENAME << std::endl; - coordinates_file << "#" << std::endl; - coordinates_file << "# Parameters" << std::endl; - coordinates_file << "# - nb. vertices: " << nb_vertices << std::endl; - coordinates_file << "# - beta: " << beta << std::endl; - coordinates_file << "# - mu: " << mu << std::endl; - coordinates_file << "# - radius_S1: " << nb_vertices / (2 * PI) << std::endl; - coordinates_file << "# - radius_H2: " << hyp_radius << std::endl; - coordinates_file << "# - kappa_min: " << kappa_min << std::endl; - coordinates_file << "# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=" << std::endl; - coordinates_file << "#"; - coordinates_file << std::setw(width_names - 1) << "Vertex" << " "; - coordinates_file << std::setw(width_values) << "Inf.Kappa" << " "; - coordinates_file << std::setw(width_values) << "Inf.Theta" << " "; - coordinates_file << std::setw(width_values) << "Inf.Hyp.Rad." << " "; - coordinates_file << std::endl; - // Structure containing the desired method to compared strings (put shorter ones before longer ones). - struct compare - { - bool operator()(const std::pair& lhs, const std::pair& rhs) const - { - if(lhs.first.size() == rhs.first.size()) - { - if(lhs.first == rhs.first) - { - return lhs.second < rhs.second; - } - else - { - return lhs.first < rhs.first; - } - } - else - { - return lhs.first.size() < rhs.first.size(); - } - } - }; - // Writes the hidden variables. - std::set< std::pair, compare > ordered_names; - for(int v(0); v >::iterator it = ordered_names.begin(); - std::set< std::pair >::iterator end = ordered_names.end(); - for(int v; it!=end; ++it) - { - v = it->second; - coordinates_file << std::setw(width_names) << it->first << " "; - coordinates_file << std::setw(width_values) << kappa[v] << " "; - coordinates_file << std::setw(width_values) << theta[v] << " "; - coordinates_file << std::setw(width_values) << hyp_radius - 2 * std::log( kappa[v] / kappa_min ) << " "; - // coordinates_file << std::setw(width) << 2 * std::log( nb_vertices / (PI * mu * kappa_min * kappa[v]) ) << " "; - coordinates_file << std::endl; - } - coordinates_file << "#" << std::endl; - coordinates_file << "# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=" << std::endl; - coordinates_file << "# Internal parameters and options" << std::endl; - coordinates_file << "# " << TAB << "ALREADY_INFERRED_PARAMETERS_FILENAME " << ALREADY_INFERRED_PARAMETERS_FILENAME << std::endl; - coordinates_file << "# " << TAB << "BETA_ABS_MAX " << BETA_ABS_MAX << std::endl; - coordinates_file << "# " << TAB << "BETA_ABS_MIN " << BETA_ABS_MIN << std::endl; - coordinates_file << "# " << TAB << "CHARACTERIZATION_MODE " << (CHARACTERIZATION_MODE ? "true" : "false") << std::endl; - coordinates_file << "# " << TAB << "CHARACTERIZATION_NB_GRAPHS " << CHARACTERIZATION_NB_GRAPHS << std::endl; - coordinates_file << "# " << TAB << "CLEAN_RAW_OUTPUT_MODE " << (CLEAN_RAW_OUTPUT_MODE ? "true" : "false") << std::endl; - // coordinates_file << "# " << TAB << "CLOSE_ANGULAR_RANGE_FACTOR " << CLOSE_ANGULAR_RANGE_FACTOR << std::endl; - coordinates_file << "# " << TAB << "CUSTOM_BETA " << (CUSTOM_BETA ? "true" : "false") << std::endl; - coordinates_file << "# " << TAB << "CUSTOM_INFERRED_COORDINATES " << (CUSTOM_INFERRED_COORDINATES ? "true" : "false") << std::endl; - coordinates_file << "# " << TAB << "CUSTOM_OUTPUT_ROOTNAME_MODE " << (CUSTOM_OUTPUT_ROOTNAME_MODE ? "true" : "false") << std::endl; - coordinates_file << "# " << TAB << "CUSTOM_SEED " << (CUSTOM_SEED ? "true" : "false") << std::endl; - coordinates_file << "# " << TAB << "EDGELIST_FILENAME: " << EDGELIST_FILENAME << std::endl; - coordinates_file << "# " << TAB << "EXP_CLUST_NB_INTEGRATION_MC_STEPS " << EXP_CLUST_NB_INTEGRATION_MC_STEPS << std::endl; - coordinates_file << "# " << TAB << "EXP_DIST_NB_INTEGRATION_STEPS " << EXP_DIST_NB_INTEGRATION_STEPS << std::endl; - coordinates_file << "# " << TAB << "KAPPA_MAX_NB_ITER_CONV " << KAPPA_MAX_NB_ITER_CONV << std::endl; - coordinates_file << "# " << TAB << "KAPPA_POST_INFERENCE_MODE " << (KAPPA_POST_INFERENCE_MODE ? "true" : "false") << std::endl; - // coordinates_file << "# " << TAB << "LIMIT_FOR_CONVERGENCE_CRITERION " << LIMIT_FOR_CONVERGENCE_CRITERION << std::endl; - // coordinates_file << "# " << TAB << "MAX_NB_ITER_MAXIMIZATION " << MAX_NB_ITER_MAXIMIZATION << std::endl; - coordinates_file << "# " << TAB << "MAXIMIZATION_MODE " << (MAXIMIZATION_MODE ? "true" : "false") << std::endl; - // coordinates_file << "# " << TAB << "MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD " << MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD << std::endl; - // coordinates_file << "# " << TAB << "MINIMAL_ANGULAR_RESOLUTION " << MINIMAL_ANGULAR_RESOLUTION << std::endl; - // coordinates_file << "# " << TAB << "NB_VERTICES_IN_CORE " << NB_VERTICES_IN_CORE << std::endl; - coordinates_file << "# " << TAB << "MIN_NB_ANGLES_TO_TRY " << MIN_NB_ANGLES_TO_TRY << std::endl; - coordinates_file << "# " << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_1 " << NUMERICAL_CONVERGENCE_THRESHOLD_1 << std::endl; - coordinates_file << "# " << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_2 " << NUMERICAL_CONVERGENCE_THRESHOLD_2 << std::endl; - coordinates_file << "# " << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_3 " << NUMERICAL_CONVERGENCE_THRESHOLD_3 << std::endl; - coordinates_file << "# " << TAB << "NUMERICAL_ZERO " << NUMERICAL_ZERO << std::endl; - coordinates_file << "# " << TAB << "QUIET_MODE " << (QUIET_MODE ? "true" : "false") << std::endl; - coordinates_file << "# " << TAB << "REFINE_MODE " << (REFINE_MODE ? "true" : "false") << std::endl; - // coordinates_file << "# " << TAB << "REFINED_MAX_STEP_LENGTH_DIVISOR " << REFINED_MAX_STEP_LENGTH_DIVISOR << std::endl; - coordinates_file << "# " << TAB << "ROOTNAME_OUTPUT: " << ROOTNAME_OUTPUT << std::endl; - coordinates_file << "# " << TAB << "SEED " << SEED << std::endl; - coordinates_file << "# " << TAB << "VALIDATION_MODE " << (VALIDATION_MODE ? "true" : "false") << std::endl; - coordinates_file << "# " << TAB << "VERBOSE_MODE " << (VERBOSE_MODE ? "true" : "false") << std::endl; - coordinates_file << "# " << TAB << "VERSION " << VERSION << std::endl; - coordinates_file << "# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=" << std::endl; - // Closes the stream. - coordinates_file.close(); - - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "=> Inferred coordinates saved to " << ROOTNAME_OUTPUT + ".inf_coord" << std::endl; } - - if(CLEAN_RAW_OUTPUT_MODE) - { - // Sets the name of the file to write the hidden variables into. - coordinates_filename = ROOTNAME_OUTPUT + ".inf_coord_raw"; - // Opens the stream and terminates if the operation did not succeed. - coordinates_file.open(coordinates_filename.c_str(), std::fstream::out); - if( !coordinates_file.is_open() ) - { - std::cerr << "Could not open file: " << coordinates_filename << "." << std::endl; - std::terminate(); - } - // Writes the hidden variables. - it = ordered_names.begin(); - end = ordered_names.end(); - for(int v; it!=end; ++it) - { - v = it->second; - // coordinates_file << it->first << " "; - coordinates_file << kappa[v] << " "; - coordinates_file << theta[v] << " "; - coordinates_file << hyp_radius - 2 * std::log( kappa[v] / kappa_min ) << " "; - coordinates_file << std::endl; - } - // Closes the stream. - coordinates_file.close(); - - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "=> Raw inferred coordinates also saved to " << ROOTNAME_OUTPUT + ".inf_coord_raw" << std::endl; } - } - - if(warning) - { - if(!QUIET_MODE) { std::clog << "WARNING: Hyperbolic radius has been adjusted to account for negative radial positions." << std::endl; } - } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::save_inferred_ensemble_characterization() -{ - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "Characterizing the inferred ensemble..." << std::endl; } - // Iterators. - std::map::iterator it2, end2; - std::map >::iterator it3, end3; - // Initializes the containers. - characterizing_inferred_ensemble_vprops.clear(); - characterizing_inferred_ensemble_vprops.resize(5); - for(int i(0); i(2, 0)); - } - characterizing_inferred_ensemble_vstat.clear(); - // Objects to compute the complementary cumulative degree distribution. - std::vector single_comp_cumul_degree_dist; - std::vector avg_comp_cumul_degree_dist; - std::vector std_comp_cumul_degree_dist; - std::vector nb_comp_cumul_degree_dist; - // Sets the number of graphs to be generated in function of the size of the original graph. - if(!CUSTOM_CHARACTERIZATION_NB_GRAPHS) - { - if (nb_vertices < 500 ) { CHARACTERIZATION_NB_GRAPHS = 1000; } - else if(nb_vertices < 1000 ) { CHARACTERIZATION_NB_GRAPHS = 500; } - else if(nb_vertices < 10000) { CHARACTERIZATION_NB_GRAPHS = 100; } - else { CHARACTERIZATION_NB_GRAPHS = 10; } - } - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "A total of " << CHARACTERIZATION_NB_GRAPHS << " graphs will be generated (chosen in function of the total number" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "of vertices). To change this value, set the flag 'CUSTOM_CHARACTERIZATION_NB_GRAPHS'" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "to 'true' and set the variable 'CHARACTERIZATION_NB_GRAPHS' to the desired value." << std::endl; } - if(!QUIET_MODE) { std::clog << std::endl; } - // Performs the simulations. - int delta_nb_graphs = CHARACTERIZATION_NB_GRAPHS / 19.999999; - if(delta_nb_graphs < 1) { delta_nb_graphs = 1; } - int width = 2 * (std::log10(CHARACTERIZATION_NB_GRAPHS) + 1) + 6; - double d1, value; - std::string graph_range; - double start_time, stop_time; - for(int g_i(0), g_f, d_max; g_i CHARACTERIZATION_NB_GRAPHS) ? CHARACTERIZATION_NB_GRAPHS : g_f; - start_time = time_since_epoch_in_seconds(); - if(!QUIET_MODE) { graph_range = "[" + std::to_string(g_i+1) + "," + std::to_string(g_f) + "]..."; } - if(!QUIET_MODE) { std::clog << TAB << "Generating and analyzing graphs " << std::setw(width) << graph_range; } - for(; g_i 0) - { - // Sum of the degree of neighbors. - value = simulated_sum_degree_of_neighbors[v1]; - characterizing_inferred_ensemble_vprops[1][v1][0] += value; - characterizing_inferred_ensemble_vprops[1][v1][1] += value * value; - // Average degree of neighbors. - value /= d1; - characterizing_inferred_ensemble_vprops[2][v1][0] += value; - characterizing_inferred_ensemble_vprops[2][v1][1] += value * value; - } - if(d1 > 1) - { - // Number of triangles attached on the vertex. - value = simulated_nb_triangles[v1]; - characterizing_inferred_ensemble_vprops[3][v1][0] += value; - characterizing_inferred_ensemble_vprops[3][v1][1] += value * value; - // Clustering coefficient. - value /= d1 * (d1 - 1) / 2; - characterizing_inferred_ensemble_vprops[4][v1][0] += value; - characterizing_inferred_ensemble_vprops[4][v1][1] += value * value; - } - } - // Compiles the various statistics about the degree classes. - it2 = simulated_stat_degree.begin(); - end2 = simulated_stat_degree.end(); - d_max = -1; - // single_comp_cumul_degree_dist.clear(); - for(int d, norm; it2!=end2; ++it2) - { - // Gets the degree class. - d = it2->first; - // Initializes the degree class if it has not been encountered yet. - if( characterizing_inferred_ensemble_vstat.find(d) == characterizing_inferred_ensemble_vstat.end() ) - { - characterizing_inferred_ensemble_vstat[d] = std::vector((2 * 5) + 1, 0); - } - // // Adjusts the size of the vector containing the complementary cumulative distribution if necessary. - if(d > d_max) - { - d_max = d; - single_comp_cumul_degree_dist.resize(d_max + 1, 0); - } - // Gets the number of vertices in this degree class. - norm = it2->second; - // Degree distribution. - value = simulated_stat_degree[d] / nb_vertices; - characterizing_inferred_ensemble_vstat[d][0] += value; - characterizing_inferred_ensemble_vstat[d][1] += value * value; - // Complementary cumulative degree distribution. - for(int q(0); q<=d; ++q) - { - single_comp_cumul_degree_dist[q] += value; - // comp_cumul_degree_dist_n[q] += 1; - } - // Sum of the degree of neighbors. - value = simulated_stat_sum_degree_neighbors[d] / norm; - characterizing_inferred_ensemble_vstat[d][2] += value; - characterizing_inferred_ensemble_vstat[d][3] += value * value; - // Average of the degree of neighbors. - value = simulated_stat_avg_degree_neighbors[d] / norm; - characterizing_inferred_ensemble_vstat[d][4] += value; - characterizing_inferred_ensemble_vstat[d][5] += value * value; - // Number of triangles attached on the vertex. - value = simulated_stat_nb_triangles[d] / norm; - characterizing_inferred_ensemble_vstat[d][6] += value; - characterizing_inferred_ensemble_vstat[d][7] += value * value; - // Clustering coefficient. - value = simulated_stat_clustering[d] / norm; - characterizing_inferred_ensemble_vstat[d][8] += value; - characterizing_inferred_ensemble_vstat[d][9] += value * value; - // Counts the number of time the degree class has been observed. - characterizing_inferred_ensemble_vstat[d][10] += 1; - } - // Counts which degree classes have been reached. - if((d_max + 1) > nb_comp_cumul_degree_dist.size()) - { - avg_comp_cumul_degree_dist.resize(d_max + 1, 0); - std_comp_cumul_degree_dist.resize(d_max + 1, 0); - nb_comp_cumul_degree_dist.resize(d_max + 1, 0); - } - for(int r(0); r<=d_max; ++r) - { - avg_comp_cumul_degree_dist[r] += single_comp_cumul_degree_dist[r]; - std_comp_cumul_degree_dist[r] += single_comp_cumul_degree_dist[r] * single_comp_cumul_degree_dist[r]; - nb_comp_cumul_degree_dist[r] += 1; - } - } - // Compiles the complementary cumulative degree distribution. - stop_time = time_since_epoch_in_seconds(); - if(!QUIET_MODE) { std::clog << "...done in " << std::setw(6) << std::fixed << stop_time - start_time << " seconds" << std::endl; } - } - // Finalizes the characterization. - for(int i(0); ifirst; - norm = characterizing_inferred_ensemble_vstat[d][10]; - for(int i(0); i<10; ++++i) - { - characterizing_inferred_ensemble_vstat[d][i + 0] /= norm; - if(norm > 1) - { - characterizing_inferred_ensemble_vstat[d][i + 1] /= norm; - characterizing_inferred_ensemble_vstat[d][i + 1] -= characterizing_inferred_ensemble_vstat[d][i + 0] * characterizing_inferred_ensemble_vstat[d][i + 0]; - characterizing_inferred_ensemble_vstat[d][i + 1] *= norm / (norm - 1); - if( characterizing_inferred_ensemble_vstat[d][i + 1] < 0 ) - { - characterizing_inferred_ensemble_vstat[d][i + 1] = 0; - } - else - { - characterizing_inferred_ensemble_vstat[d][i + 1] = std::sqrt( characterizing_inferred_ensemble_vstat[d][i + 1] ); - } - } - else - { - characterizing_inferred_ensemble_vstat[d][i + 1] = 0; - } - } - } - // Complete the characterization of the complementary cumulative degree distribution. - for(int i(0), ii(nb_comp_cumul_degree_dist.size()); i 0) - { - avg_comp_cumul_degree_dist[i] /= nb_comp_cumul_degree_dist[i]; - if(nb_comp_cumul_degree_dist[i] > 1) - { - std_comp_cumul_degree_dist[i] /= nb_comp_cumul_degree_dist[i]; - std_comp_cumul_degree_dist[i] -= avg_comp_cumul_degree_dist[i] * avg_comp_cumul_degree_dist[i]; - std_comp_cumul_degree_dist[i] *= nb_comp_cumul_degree_dist[i] / (nb_comp_cumul_degree_dist[i] - 1); - if(std_comp_cumul_degree_dist[i] < 0) - { - std_comp_cumul_degree_dist[i] = 0; - } - else - { - std_comp_cumul_degree_dist[i] = std::sqrt(std_comp_cumul_degree_dist[i]); - } - } - else - { - std_comp_cumul_degree_dist[i] = 0; - } - } - } - if(!QUIET_MODE) { std::clog << " ...............................................done." << std::endl; } - // Sets the name of the file to write the vertices properties into. - std::string vertex_properties_filename = ROOTNAME_OUTPUT + ".inf_vprop"; - // Opens the stream and terminates if the operation did not succeed. - std::fstream vertex_properties_file(vertex_properties_filename.c_str(), std::fstream::out); - if( !vertex_properties_file.is_open() ) - { - std::cerr << "Could not open file: " << vertex_properties_filename << "." << std::endl; - std::terminate(); - } - // Writes the header. - vertex_properties_file << "#"; - vertex_properties_file << std::setw(width_names - 1) << "Vertex" << " "; - vertex_properties_file << std::setw(width_values) << "Degree" << " "; - vertex_properties_file << std::setw(width_values) << "Avg.Degree" << " "; - vertex_properties_file << std::setw(width_values) << "Std.Degree" << " "; - vertex_properties_file << std::setw(width_values) << "Sum.Deg.N" << " "; - vertex_properties_file << std::setw(width_values) << "Avg.Sum.Deg.N" << " "; - vertex_properties_file << std::setw(width_values) << "Std.Sum.Deg.N" << " "; - vertex_properties_file << std::setw(width_values) << "Avg.Deg.N" << " "; - vertex_properties_file << std::setw(width_values) << "Avg.Avg.Deg.N" << " "; - vertex_properties_file << std::setw(width_values) << "Std.Avg.Deg.N" << " "; - vertex_properties_file << std::setw(width_values) << "NbTriang" << " "; - vertex_properties_file << std::setw(width_values) << "Avg.NbTriang" << " "; - vertex_properties_file << std::setw(width_values) << "Std.NbTriang" << " "; - vertex_properties_file << std::setw(width_values) << "Clustering" << " "; - vertex_properties_file << std::setw(width_values) << "Avg.Clustering" << " "; - vertex_properties_file << std::setw(width_values) << "Std.Clustering" << " "; - vertex_properties_file << std::endl; - // Structure containing the desired method to compared strings (put shorter ones before longer ones). - struct compare - { - bool operator()(const std::pair& lhs, const std::pair& rhs) const - { - if(lhs.first.size() == rhs.first.size()) - { - if(lhs.first == rhs.first) - { - return lhs.second < rhs.second; - } - else - { - return lhs.first < rhs.first; - } - } - else - { - return lhs.first.size() < rhs.first.size(); - } - } - }; - // Writes the hidden variables. - std::set< std::pair, compare > ordered_names; - for(int v(0); v >::iterator it = ordered_names.begin(); - std::set< std::pair >::iterator end = ordered_names.end(); - for(int v, d; it!=end; ++it) - { - v = it->second; - d = degree[v]; - vertex_properties_file << std::setw(width_names) << it->first << " "; - vertex_properties_file << std::setw(width_values) << degree[v] << " "; - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[0][v][0] << " "; - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[0][v][1] << " "; - vertex_properties_file << std::setw(width_values) << sum_degree_of_neighbors[v] << " "; - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[1][v][0] << " "; - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[1][v][1] << " "; - if(d > 0) - { - vertex_properties_file << std::setw(width_values) << sum_degree_of_neighbors[v] / d << " "; - } - else - { - vertex_properties_file << std::setw(width_values) << sum_degree_of_neighbors[v] << " "; - } - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[2][v][0] << " "; - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[2][v][1] << " "; - vertex_properties_file << std::setw(width_values) << nbtriangles[v] << " "; - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[3][v][0] << " "; - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[3][v][1] << " "; - if(d > 1) - { - vertex_properties_file << std::setw(width_values) << nbtriangles[v] / (d * (d-1) / 2) << " "; - } - else - { - vertex_properties_file << std::setw(width_values) << nbtriangles[v] << " "; - } - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[4][v][0] << " "; - vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[4][v][1] << " "; - vertex_properties_file << std::endl; - } - vertex_properties_file.close(); - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "=> Vertices properties of the inferred ensemble saved to " << ROOTNAME_OUTPUT + ".inf_vprop" << std::endl; } - - // Sets the name of the file to write the vertices properties into. - std::string vertex_stat_filename = ROOTNAME_OUTPUT + ".inf_vstat"; - // Opens the stream and terminates if the operation did not succeed. - std::fstream vertex_stat_file(vertex_stat_filename.c_str(), std::fstream::out); - if( !vertex_stat_file.is_open() ) - { - std::cerr << "Could not open file: " << vertex_stat_filename << "." << std::endl; - std::terminate(); - } - // Writes the header. - vertex_stat_file << "#"; - vertex_stat_file << std::setw(width_values - 1) << "Degree" << " "; - // vertex_stat_file << std::setw(width_values) << "DegDistObs" << " "; - vertex_stat_file << std::setw(width_values) << "DegDistEns" << " "; - vertex_stat_file << std::setw(width_values) << "DegDistEnsStd" << " "; - vertex_stat_file << std::setw(width_values) << "CDegDistEns" << " "; - vertex_stat_file << std::setw(width_values) << "CDegDistEnsStd" << " "; - // vertex_stat_file << std::setw(width_values) << "SumDegNObs" << " "; - vertex_stat_file << std::setw(width_values) << "SumDegNEns" << " "; - vertex_stat_file << std::setw(width_values) << "SumDegNEnsStd" << " "; - // vertex_stat_file << std::setw(width_values) << "AvgDegNObs" << " "; - vertex_stat_file << std::setw(width_values) << "AvgDegNEns" << " "; - vertex_stat_file << std::setw(width_values) << "AvgDegNEnsStd" << " "; - // vertex_stat_file << std::setw(width_values) << "NbTriangObs" << " "; - vertex_stat_file << std::setw(width_values) << "NbTriangEns" << " "; - vertex_stat_file << std::setw(width_values) << "NbTriangEnsStd" << " "; - // vertex_stat_file << std::setw(width_values) << "ClustObs" << " "; - vertex_stat_file << std::setw(width_values) << "ClustEns" << " "; - vertex_stat_file << std::setw(width_values) << "ClustEnsStd" << " "; - vertex_stat_file << std::endl; - // Writes the hidden variables. - it3 = characterizing_inferred_ensemble_vstat.begin(); - end3 = characterizing_inferred_ensemble_vstat.end(); - for(int v, d; it3!=end3; ++it3) - { - d = it3->first; - vertex_stat_file << std::setw(width_values) << d << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][0] << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][1] << " "; - vertex_stat_file << std::setw(width_values) << avg_comp_cumul_degree_dist[d] << " "; - vertex_stat_file << std::setw(width_values) << std_comp_cumul_degree_dist[d] << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][2] << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][3] << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][4] << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][5] << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][6] << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][7] << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][8] << " "; - vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][9] << " "; - vertex_stat_file << std::endl; - } - vertex_stat_file.close(); - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "=> Inferred ensemble statistics by degree class saved to " << ROOTNAME_OUTPUT + ".inf_vstat" << std::endl; } - - - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << "Extracting the original graph statistics by degree class..."; } - - - // Extracts the vertex statistics of the original edgelist by degree class. - std::map original_stat_degree; - std::map original_stat_sum_degree_neighbors; - std::map original_stat_avg_degree_neighbors; - std::map original_stat_nb_triangles; - std::map original_stat_clustering; - for(int v1(0), d1; v1 0) - { - original_stat_sum_degree_neighbors[d1] += sum_degree_of_neighbors[v1]; - original_stat_avg_degree_neighbors[d1] += sum_degree_of_neighbors[v1] / d1; - } - if(d1 > 1) - { - original_stat_nb_triangles[d1] += nbtriangles[v1]; - original_stat_clustering[d1] += 2 * nbtriangles[v1] / d1 / (d1 - 1); - } - } - if(!QUIET_MODE) { std::clog << "...........................done." << std::endl; } - - // Sets the name of the file to write the vertices properties into. - std::string graph_stat_filename = ROOTNAME_OUTPUT + ".obs_vstat"; - // Opens the stream and terminates if the operation did not succeed. - std::fstream graph_stat_file(graph_stat_filename.c_str(), std::fstream::out); - if( !graph_stat_file.is_open() ) - { - std::cerr << "Could not open file: " << graph_stat_filename << "." << std::endl; - std::terminate(); - } - - // Writes the header. - graph_stat_file << "#"; - graph_stat_file << std::setw(width_values - 1) << "Degree" << " "; - graph_stat_file << std::setw(width_values) << "DegDist" << " "; - graph_stat_file << std::setw(width_values) << "CDegDist" << " "; - graph_stat_file << std::setw(width_values) << "SumDegN" << " "; - graph_stat_file << std::setw(width_values) << "AvgDegN" << " "; - graph_stat_file << std::setw(width_values) << "NbTriang" << " "; - graph_stat_file << std::setw(width_values) << "Clust" << " "; - graph_stat_file << std::endl; - // Writes the hidden variables. - double ccdegdist = 1; - it2 = original_stat_degree.begin(); - end2 = original_stat_degree.end(); - for(int v, d, norm; it2!=end2; ++it2) - { - d = it2->first; - norm = it2->second; - graph_stat_file << std::setw(width_values) << d << " "; - graph_stat_file << std::setw(width_values) << original_stat_degree[d] / nb_vertices << " "; - graph_stat_file << std::setw(width_values) << ccdegdist << " "; - ccdegdist -= original_stat_degree[d] / nb_vertices; - graph_stat_file << std::setw(width_values) << original_stat_sum_degree_neighbors[d] / norm << " "; - graph_stat_file << std::setw(width_values) << original_stat_avg_degree_neighbors[d] / norm << " "; - graph_stat_file << std::setw(width_values) << original_stat_nb_triangles[d] / norm << " "; - graph_stat_file << std::setw(width_values) << original_stat_clustering[d] / norm << " "; - graph_stat_file << std::endl; - } - graph_stat_file.close(); - - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "=> Original graph statistics by degree class saved to " << ROOTNAME_OUTPUT + ".obs_vstat" << std::endl; } - // // Gets the current time. - // time2 = std::time(NULL); -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::save_inferred_theta_density() -{ - // Builds the bins. - std::map bins; - std::map::iterator it; - int cnt = 0; - int nb_bins = 25; - double dt = 2 * PI / nb_bins; - for(double t(dt), tt(2 * PI + 0.001); t n(bins.size(), 0); - // Computes the connection probability for every pair of vertices. - for(int v1(0), i; v1second; - n[i] += 1; - } - // Writes the connection probability into a file. - std::string theta_density_filename = ROOTNAME_OUTPUT + ".inf_theta_density"; - std::fstream theta_density_file(theta_density_filename.c_str(), std::fstream::out); - if( !theta_density_file.is_open() ) - { - std::cerr << "Could not open file: " << theta_density_filename << "." << std::endl; - std::terminate(); - } - theta_density_file << "#"; - theta_density_file << std::setw(width_values - 1) << "Theta" << " "; - theta_density_file << std::setw(width_values) << "InfDensity" << " "; - theta_density_file << std::setw(width_values) << "ThDensity" << " "; - theta_density_file << std::endl; - for(int i(0), ii(n.size()); i Inferred theta density saved to " << ROOTNAME_OUTPUT + ".inf_theta_density" << std::endl; } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -double embeddingS1_t::time_since_epoch_in_seconds() -{ - // double tmp = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - // return tmp / 1000; - // https://en.cppreference.com/w/cpp/chrono/c/clock - clock_t t = clock(); - return ((float)t) / (CLOCKS_PER_SEC); -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -int embeddingS1_t::get_root(int i, std::vector &clust_id) -{ - while(i != clust_id[i]) - { - clust_id[i] = clust_id[clust_id[i]]; - i = clust_id[i]; - } - return i; -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::merge_clusters(std::vector &size, std::vector &clust_id) -{ - // Variables. - int v1, v2, v3, v4; - // Iterators. - std::set::iterator it, end; - // Loops over the vertices. - for(int i(0); i size[v1]) - std::swap(v1, v2); - v3 = get_root(v1, clust_id); - v4 = get_root(v2, clust_id); - clust_id[v4] = v3; - size[v3] += size[v4]; - } - } - } -} - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void embeddingS1_t::check_connected_components() -{ - // Vector containing the ID of the component to which each node belongs. - std::vector Vertex2Prop(nb_vertices, -1); - - // Vector containing the size of the components. - std::vector connected_components_size; - - // Set ordering the component according to their size. - std::set< std::pair > ordered_connected_components; - - // Starts with every vertex as an isolated cluster. - std::vector clust_id(nb_vertices); - std::vector clust_size(nb_vertices, 1); - for(int v(0); v CompID; - for(int v(0); vsecond; - int lcc_size = (--ordered_connected_components.end())->first; - - if(lcc_size != nb_vertices) - { - if(!QUIET_MODE) { std::clog << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "- More than one component found!!" << std::endl; } - if(!QUIET_MODE) { std::clog << TAB << "- " << lcc_size << "/" << nb_vertices << " vertices in the largest component." << std::endl; } - std::cerr << std::endl; - std::cerr << "More than one component found (" << lcc_size << "/" << nb_vertices << " vertices in the largest component." << std::endl; - - std::string edgelist_rootname; - size_t lastdot = EDGELIST_FILENAME.find_last_of("."); - if(lastdot == std::string::npos) - { - edgelist_rootname = EDGELIST_FILENAME; - } - edgelist_rootname = EDGELIST_FILENAME.substr(0, lastdot); - - // Sets the name of the file to write the hidden variables into. - std::string edgelist_filename = edgelist_rootname + "_GC.edge"; - // Opens the stream and terminates if the operation did not succeed. - std::fstream edgelist_file(edgelist_filename.c_str(), std::fstream::out); - if( !edgelist_file.is_open() ) - { - std::cerr << "Could not open file: " << edgelist_filename << "." << std::endl; - std::terminate(); - } - - std::set::iterator it, end; - for(int v1(0), v2, c1, c2; v1 degree[v2]) -// { -// std::swap(v1, v2); -// } -// // Computes the loglikelihood between vertives v1 and v2 (checks with the vertex with smaller degree). -// if(adjacency_list[v1].find(v2) == adjacency_list[v1].end()) // not neighbors -// { -// return -1 * std::log( 1 + std::pow( (nb_vertices * da) / (2 * PI * mu * kappa[v1] * kappa[v2]), -beta) ); -// } -// else // neighbors -// { -// return -1 * std::log( 1 + std::pow( (nb_vertices * da) / (2 * PI * mu * kappa[v1] * kappa[v2]), beta) ); -// } -// } - - -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// double embeddingS1_t::find_optimal_angle(int v1) -// { -// // Variables. -// double tmp_angle; -// double tmp_loglikelihood; -// double previous_angle = theta[v1]; -// double best_angle = previous_angle; -// // double max_step_length = 2 * PI / MINIMAL_ANGULAR_RESOLUTION; -// double max_step_length = 2 * 2 * PI / nb_vertices; -// // Computes the current loglikelihood. -// double previous_loglikelihood = 0; -// for(int v2(0); v2 (2 * PI)) ? (t1 - (2 * PI)) : t1; -// // Computes the local loglikelihood. -// tmp_loglikelihood = 0; -// for(int v2(0); v2 best_loglikelihood) -// { -// best_loglikelihood = tmp_loglikelihood; -// best_angle = tmp_angle; -// } -// // Increases the angle. -// t1 += max_step_length * uniform_01(engine); -// } -// // // Refines the position around the best position found above. -// // double close_angular_range = CLOSE_ANGULAR_RANGE_FACTOR * 2 * PI / nb_vertices; -// // double refined_max_step_length = 2 * PI / nb_vertices / REFINED_MAX_STEP_LENGTH_DIVISOR; -// // t0 = best_angle - close_angular_range ; -// // t0 = (t0 < 0) ? (t0 + (2 * PI)) : t0; -// // t1 = t0; -// // while( (t1 - t0) < (2 * close_angular_range) ) -// // { -// // // Gets the angle in the standard range. -// // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; -// // // Computes the local loglikelihood. -// // tmp_loglikelihood = 0; -// // for(int v2(0); v2 best_loglikelihood) -// // { -// // best_loglikelihood = tmp_loglikelihood; -// // best_angle = tmp_angle; -// // } -// // // Increases the angle. -// // t1 += refined_max_step_length * uniform_01(engine); -// // } -// // Registers the best position found. -// theta[v1] = best_angle; -// // Returns the variation in the global loglikelihood. -// // return (best_loglikelihood - previous_loglikelihood) / nb_edges; -// return PI - std::fabs( PI - std::fabs(best_angle - previous_angle) ); -// } - - -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// void embeddingS1_t::refine_angle5(int v1) -// { -// // Variables. -// double tmp_angle; -// double tmp_loglikelihood; -// double best_angle = theta[v1]; -// // Computes the current loglikelihood. -// double previous_loglikelihood = 0; -// for(int v2(0); v2::iterator it2 = adjacency_list[v1].begin(); -// std::set::iterator end = adjacency_list[v1].end(); -// double t2, k2, da; -// double sum_sin_theta = 0; -// double sum_cos_theta = 0; -// for(; it2!=end; ++it2) -// { -// // Identifies the neighbor. -// t2 = theta[*it2]; -// k2 = kappa[*it2]; - -// // Computes the average angle of neighbors. -// sum_sin_theta += std::sin(t2) / (k2 * k2); -// sum_cos_theta += std::cos(t2) / (k2 * k2); -// } -// double average_theta = std::atan2(sum_sin_theta, sum_cos_theta); -// while(average_theta > (2 * PI)) -// average_theta = average_theta - (2 * PI); -// while(average_theta < 0) -// average_theta = average_theta + (2 * PI); - -// // Finds the largest angular distance between the neighbor and the average position. -// // double max_angle = PI / 4; -// double max_angle = PI / 6; -// it2 = adjacency_list[v1].begin(); -// for(; it2!=end; ++it2) -// { -// da = PI - std::fabs(PI - std::fabs(average_theta - theta[*it2])); -// if(da > max_angle) -// { -// max_angle = da; -// } -// } -// max_angle /= 2; - -// // std::clog << average_theta << " " << max_angle << std::endl; -// // std::clog.flush(); - -// // Considers various wisely chosen new angular positions and keeps the best. -// int _nb_new_angles_to_try = NB_NEW_ANGLES_TO_TRY * std::log(nb_vertices); -// // int _nb_new_angles_to_try = max_angle * nb_vertices / PI; -// // if(_nb_new_angles_to_try < NB_NEW_ANGLES_TO_TRY) -// // _nb_new_angles_to_try = NB_NEW_ANGLES_TO_TRY; -// for(int e(0); e<_nb_new_angles_to_try; ++e) -// { -// // Gets the angle in the standard range. -// tmp_angle = (normal_01(engine) * max_angle) + average_theta; -// while(tmp_angle > (2 * PI)) -// tmp_angle = tmp_angle - (2 * PI); -// while(tmp_angle < 0) -// tmp_angle = tmp_angle + (2 * PI); - -// // Computes the local loglikelihood. -// tmp_loglikelihood = 0; -// for(int v2(0); v2 best_loglikelihood) -// { -// best_loglikelihood = tmp_loglikelihood; -// best_angle = tmp_angle; -// } -// } - -// // Registers the best position found. -// theta[v1] = best_angle; -// } - - -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// void embeddingS1_t::refine_angle3(int v1) -// { -// // Variables. -// double tmp_angle; -// double tmp_loglikelihood; -// double previous_angle = theta[v1]; -// double best_angle = previous_angle; -// // double max_step_length = 2 * PI / MINIMAL_ANGULAR_RESOLUTION; -// double max_step_length = 2 * 2 * PI / nb_vertices; -// // Computes the current loglikelihood. -// double previous_loglikelihood = 0; -// for(int v2(0); v2 (2 * PI)) ? (t1 - (2 * PI)) : t1; -// tmp_angle = t1; -// while(tmp_angle > (2 * PI)) -// tmp_angle = tmp_angle - (2 * PI); -// while(tmp_angle < 0) -// tmp_angle = tmp_angle + (2 * PI); -// // Computes the local loglikelihood. -// tmp_loglikelihood = 0; -// for(int v2(0); v2 best_loglikelihood) -// { -// best_loglikelihood = tmp_loglikelihood; -// best_angle = tmp_angle; -// } -// // Increases the angle. -// t1 += max_step_length * uniform_01(engine); -// } -// // // Refines the position around the best position found above. -// // // double close_angular_range = CLOSE_ANGULAR_RANGE_FACTOR * 2 * PI / nb_vertices; -// // // double refined_max_step_length = 2 * PI / nb_vertices / REFINED_MAX_STEP_LENGTH_DIVISOR; -// // // t0 = best_angle - close_angular_range ; -// // t0 = best_angle_prov - (200 * max_step_length) ; -// // // t0 = (t0 < 0) ? (t0 + (2 * PI)) : t0; -// // while(t0 > (2 * PI)) -// // t0 = t0 - (2 * PI); -// // while(t0 < 0) -// // t0 = t0 + (2 * PI); -// // t1 = t0; -// // while( (t1 - t0) < (2 * 200 * max_step_length) ) -// // { -// // // Gets the angle in the standard range. -// // // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; -// // tmp_angle = t1; -// // while(tmp_angle > (2 * PI)) -// // tmp_angle = tmp_angle - (2 * PI); -// // while(tmp_angle < 0) -// // tmp_angle = tmp_angle + (2 * PI); -// // // Computes the local loglikelihood. -// // tmp_loglikelihood = 0; -// // for(int v2(0); v2 best_loglikelihood) -// // { -// // best_loglikelihood = tmp_loglikelihood; -// // best_angle = tmp_angle; -// // } -// // // Increases the angle. -// // t1 += max_step_length * uniform_01(engine); -// // } -// // Registers the best position found. -// theta[v1] = best_angle; -// // Returns the variation in the global loglikelihood. -// // return (best_loglikelihood - previous_loglikelihood) / nb_edges; -// // return PI - std::fabs( PI - std::fabs(best_angle - previous_angle) ); -// } - - -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// void embeddingS1_t::refine_angle4(int v1) -// { -// // Variables. -// double tmp_angle; -// double best_angle_prov; -// double tmp_loglikelihood; -// double tmp_partial_loglikelihood; -// double previous_angle = theta[v1]; -// double best_angle = previous_angle; -// // double max_step_length = 2 * PI / MINIMAL_ANGULAR_RESOLUTION; -// double max_step_length = 2 * 2 * PI / nb_vertices; -// // Computes the current loglikelihood. -// double previous_loglikelihood = 0; -// for(int v2(0); v2::iterator it2; -// std::set::iterator end; -// double best_partial_loglikelihood = -1e100; -// double t0 = 2 * PI * uniform_01(engine); -// double t1 = t0; -// while( (t1 - t0) < (2 * PI) ) -// { -// // Gets the angle in the standard range. -// // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; -// tmp_angle = t1; -// while(tmp_angle >= (2 * PI)) -// tmp_angle = tmp_angle - (2 * PI); -// // while(tmp_angle < 0) -// // tmp_angle = tmp_angle + (2 * PI); -// // Computes the local loglikelihood. -// tmp_partial_loglikelihood = 0; -// it2 = adjacency_list[v1].begin(); -// end = adjacency_list[v1].end(); -// for(; it2!=end; ++it2) -// { -// tmp_partial_loglikelihood += compute_pairwise_loglikelihood(v1, tmp_angle, *it2, theta[*it2]); -// } -// // std::cout << tmp_partial_loglikelihood << std::endl; -// // Preserves the optimal angular sector. -// if(tmp_partial_loglikelihood > best_partial_loglikelihood) -// { -// best_partial_loglikelihood = tmp_partial_loglikelihood; -// best_angle_prov = tmp_angle; -// } -// // Increases the angle. -// t1 += max_step_length * uniform_01(engine); -// } -// // Refines the position around the best position found above. -// // double close_angular_range = CLOSE_ANGULAR_RANGE_FACTOR * 2 * PI / nb_vertices; -// // double refined_max_step_length = 2 * PI / nb_vertices / REFINED_MAX_STEP_LENGTH_DIVISOR; -// // t0 = best_angle - close_angular_range ; -// // t0 = best_angle_prov - (250 * max_step_length) ; -// t0 = best_angle_prov - (PI / 12) ; -// // t0 = (t0 < 0) ? (t0 + (2 * PI)) : t0; -// // while(t0 > (2 * PI)) -// // t0 = t0 - (2 * PI); -// while(t0 < 0) -// t0 = t0 + (2 * PI); -// t1 = t0; -// // while( (t1 - t0) < (2 * 250 * max_step_length) ) -// while( (t1 - t0) < (2 * PI / 12) ) -// { -// // Gets the angle in the standard range. -// // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; -// tmp_angle = t1; -// while(tmp_angle >= (2 * PI)) -// tmp_angle = tmp_angle - (2 * PI); -// // while(tmp_angle < 0) -// // tmp_angle = tmp_angle + (2 * PI); -// // Computes the local loglikelihood. -// tmp_loglikelihood = 0; -// for(int v2(0); v2 best_loglikelihood) -// { -// best_loglikelihood = tmp_loglikelihood; -// best_angle = tmp_angle; -// } -// // Increases the angle. -// t1 += max_step_length * uniform_01(engine); -// } -// // Registers the best position found. -// theta[v1] = best_angle; -// // Returns the variation in the global loglikelihood. -// // return (best_loglikelihood - previous_loglikelihood) / nb_edges; -// // return PI - std::fabs( PI - std::fabs(best_angle - previous_angle) ); -// } - - -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// void embeddingS1_t::refine_angle2(int v1) -// { -// // Variables. -// double tmp_angle; -// double tmp_loglikelihood; -// double best_angle = theta[v1]; -// // Computes the current loglikelihood. -// double previous_loglikelihood = 0; -// for(int v2(0); v2::iterator it2 = adjacency_list[v1].begin(); -// std::set::iterator end = adjacency_list[v1].end(); -// double t2, k2, da; -// double sum_sin_theta = 0; -// double sum_cos_theta = 0; -// for(; it2!=end; ++it2) -// { -// // Identifies the neighbor. -// t2 = theta[*it2]; -// k2 = kappa[*it2]; - -// // Computes the average angle of neighbors. -// sum_sin_theta += std::sin(t2) / (k2 * k2); -// sum_cos_theta += std::cos(t2) / (k2 * k2); -// } -// double average_theta = std::atan2(sum_sin_theta, sum_cos_theta); -// while(average_theta > (2 * PI)) -// average_theta = average_theta - (2 * PI); -// while(average_theta < 0) -// average_theta = average_theta + (2 * PI); - -// // Finds the largest angular distance between the neighbor and the average position. -// double max_angle = PI / 6; -// // double max_angle = PI / 12; -// it2 = adjacency_list[v1].begin(); -// for(; it2!=end; ++it2) -// { -// da = PI - std::fabs(PI - std::fabs(average_theta - theta[*it2])); -// if(da > max_angle) -// { -// max_angle = da; -// } -// } -// max_angle /= 2; - -// // std::clog << average_theta << " " << max_angle << std::endl; -// // std::clog.flush(); - -// // Considers various wisely chosen new angular positions and keeps the best. -// int _nb_new_angles_to_try = max_angle * nb_vertices / PI; -// if(_nb_new_angles_to_try < NB_NEW_ANGLES_TO_TRY) -// _nb_new_angles_to_try = NB_NEW_ANGLES_TO_TRY; -// for(int e(0); e<_nb_new_angles_to_try; ++e) -// { -// // Gets the angle in the standard range. -// tmp_angle = (normal_01(engine) * max_angle) + average_theta; -// while(tmp_angle > (2 * PI)) -// tmp_angle = tmp_angle - (2 * PI); -// while(tmp_angle < 0) -// tmp_angle = tmp_angle + (2 * PI); - -// // Computes the local loglikelihood. -// tmp_loglikelihood = 0; -// for(int v2(0); v2 best_loglikelihood) -// { -// best_loglikelihood = tmp_loglikelihood; -// best_angle = tmp_angle; -// } -// } - -// // Registers the best position found. -// theta[v1] = best_angle; -// } - - -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// void embeddingS1_t::infer_optimal_positions() -// { -// // Sets the convergence threshold. -// double maximization_convergence_threshold = MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD; -// // if(nb_vertices < LIMIT_FOR_CONVERGENCE_CRITERION) -// // { -// // maximization_convergence_threshold = MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD; -// // // maximization_convergence_threshold = 2 * PI / nb_vertices; -// // } -// // else -// // { -// // maximization_convergence_threshold = 2 * PI / std::sqrt(LIMIT_FOR_CONVERGENCE_CRITERION * nb_vertices); -// // } -// if(!QUIET_MODE) { std::clog << "Maximizing positions..."; } -// if(!QUIET_MODE) { std::clog << std::endl; } -// if(!QUIET_MODE) { std::clog << TAB << "Convergence threshold: " << maximization_convergence_threshold << std::endl; } -// if(!QUIET_MODE) { std::clog << TAB << " "; } -// if(!QUIET_MODE) { std::clog << "Average angular displacement by centrality"; } -// if(!QUIET_MODE) { std::clog << TAB << " "; } -// if(!QUIET_MODE) { std::clog << "Distribution of the angular displacement"; } -// if(!QUIET_MODE) { std::clog << std::endl; } -// if(!QUIET_MODE) { std::clog << TAB; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "iter." << " "; } -// if(!QUIET_MODE) { std::clog << TAB; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[1,20)" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[20,40)" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[40,60)" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[60,80)" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[80,100]" << " "; } -// if(!QUIET_MODE) { std::clog << TAB; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "min" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "5th" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "25th" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "median" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "average" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "75th" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "95th" << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "max" << " "; } -// if(!QUIET_MODE) { std::clog << TAB; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(12) << "time (sec.)" << " "; } -// if(!QUIET_MODE) { std::clog << std::endl; } -// // Variables. -// time_t start_time, stop_time; -// double avg_diff_angle; -// double avg_diff_angle_per_sector; -// int nb_iterations = 1; -// int index_05 = (0.05 * nb_vertices) - 1; -// int index_25 = (0.25 * nb_vertices) - 1; -// int index_50 = (0.50 * nb_vertices) - 1; -// int index_75 = (0.75 * nb_vertices) - 1; -// int index_95 = (0.95 * nb_vertices) - 1; -// std::vector angular_displacement(nb_vertices, 10); -// bool keep_going = true; -// while( keep_going ) -// { -// // // Looks for inversions and correct them. -// // correct_inversions(); -// start_time = std::time(NULL); -// if(!QUIET_MODE) { std::clog << TAB; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << nb_iterations << " "; } -// if(!QUIET_MODE) { std::clog.flush(); } -// // Finds the best angle taking into account the position of other vertices. -// avg_diff_angle = 0; -// for(int i(0); (i nb_vertices) ? (nb_vertices) : i_end; -// avg_diff_angle_per_sector = 0; -// for(int j(i_begin); j MAXIMIZATION_CONVERGENCE_THRESHOLD) -// if(avg_diff_angle_per_sector > maximization_convergence_threshold) -// { -// keep_going = true; -// } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << avg_diff_angle_per_sector << " "; } -// } -// // Orders the angular displacements. -// std::sort(angular_displacement.begin(), angular_displacement.end()); -// // Characterizes the dispersion. -// if(!QUIET_MODE) { std::clog << TAB; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[0] << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_05] << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_25] << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_50] << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << avg_diff_angle << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_75] << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_95] << " "; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[nb_vertices - 1] << " "; } -// // Time elapsed. -// stop_time = std::time(NULL); -// if(!QUIET_MODE) { std::clog << TAB; } -// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(12) << stop_time - start_time << " "; } -// if(!QUIET_MODE) { std::clog << std::endl; } -// // Updates the number of iterations. -// ++nb_iterations; -// if(nb_iterations == MAX_NB_ITER_MAXIMIZATION) -// { -// if(!QUIET_MODE) { std::clog << "WARNING: maximum number of iterations reached" << std::endl; } -// break; -// } -// } -// if(!QUIET_MODE) { std::clog << " ...............................................................done." << std::endl; } -// if(!QUIET_MODE) { std::clog << std::endl; } -// } - - -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// double embeddingS1_t::find_approximate_optimal_angle(int v1) -// { -// // Variables. -// double tmp_angle; -// double tmp_loglikelihood; -// double previous_angle = theta[v1]; -// double best_angle = previous_angle; -// // double max_step_length = 2 * PI / MINIMAL_ANGULAR_RESOLUTION; -// double max_step_length = 2 * 2 * PI / nb_vertices; -// // Iterators. -// std::set::iterator it, end; -// // Computes the current loglikelihood. -// double previous_loglikelihood = 0; -// it = adjacency_list[v1].begin(); -// end = adjacency_list[v1].end(); -// for(int v2; it!=end; ++it) -// { -// v2 = *it; -// previous_loglikelihood += compute_pairwise_loglikelihood(v1, best_angle, v2, theta[v2]); -// } -// double best_loglikelihood = previous_loglikelihood; -// // For "all" angles, starting at a random position. -// double t0 = 2 * PI * uniform_01(engine); -// double t1 = t0; -// while( (t1 - t0) < (2 * PI) ) -// { -// // Gets the angle in the standard range. -// tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; -// // Computes the local loglikelihood. -// tmp_loglikelihood = 0; -// it = adjacency_list[v1].begin(); -// end = adjacency_list[v1].end(); -// for(int v2; it!=end; ++it) -// { -// v2 = *it; -// tmp_loglikelihood += compute_pairwise_loglikelihood(v1, tmp_angle, v2, theta[v2]); -// } -// // Preserves the optimal angular sector. -// if(tmp_loglikelihood > best_loglikelihood) -// { -// best_loglikelihood = tmp_loglikelihood; -// best_angle = tmp_angle; -// } -// // Increases the angle. -// t1 += max_step_length * uniform_01(engine); -// } -// // // Refines the position around the best position found above. -// // double close_angular_range = CLOSE_ANGULAR_RANGE_FACTOR * 2 * PI / nb_vertices; -// // double refined_max_step_length = 2 * PI / nb_vertices / REFINED_MAX_STEP_LENGTH_DIVISOR; -// // t0 = best_angle - close_angular_range ; -// // t0 = (t0 < 0) ? (t0 + (2 * PI)) : t0; -// // t1 = t0; -// // while( (t1 - t0) < (2 * close_angular_range) ) -// // { -// // // Gets the angle in the standard range. -// // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; -// // // Computes the local loglikelihood. -// // tmp_loglikelihood = 0; -// // it = adjacency_list[v1].begin(); -// // end = adjacency_list[v1].end(); -// // for(int v2; it!=end; ++it) -// // { -// // v2 = *it; -// // tmp_loglikelihood += compute_pairwise_loglikelihood(v1, tmp_angle, v2, theta[v2]); -// // } -// // // Preserves the optimal angular sector. -// // if(tmp_loglikelihood > best_loglikelihood) -// // { -// // best_loglikelihood = tmp_loglikelihood; -// // best_angle = tmp_angle; -// // } -// // // Increases the angle. -// // t1 += refined_max_step_length * uniform_01(engine); -// // } -// // Registers the best position found. -// theta[v1] = best_angle; -// // Returns the variation in the global loglikelihood. -// // return (best_loglikelihood - previous_loglikelihood) / nb_edges; -// return PI - std::fabs( PI - std::fabs(best_angle - previous_angle) ); -// } +/* + * + * This class provides the functions to embed a graph in the S1 space. + * + * Compilation requires the c++11 standard (to use #include ), the Eigen, the Spectra as + * well as the hyp2f1.a library. + * + * Author: Antoine Allard + * WWW: antoineallard.info + * Version: 1.0 + * Date: May 2018 + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + */ + +#ifndef EMBEDDINGS1_HPP_INCLUDED +#define EMBEDDINGS1_HPP_INCLUDED + +// Standard Template Library +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// Eigen library +#include "Eigen/Core" +#include "Eigen/SparseCore" +// Spectra library +#include "Spectra/GenEigsSolver.h" +#include "Spectra/MatOp/SparseGenMatProd.h" +// Custom library for specific Gaussian hypergeometric functions. +#include "hyp2f1.hpp" + + +class embeddingS1_t +{ + private: + // pi + const double PI = 3.141592653589793238462643383279502884197; + // Flags controlling options. + public: + // Characterizes the inferred ensemble (generates CHARACTERIZATION_NB_GRAPHS graphs and measure + // various structural properties). + bool CHARACTERIZATION_MODE = false; + // Also write a raw file containing the inferred coordinates without any "commented" lines. + bool CLEAN_RAW_OUTPUT_MODE = false; + // Has a custom value for beta been provided? + bool CUSTOM_BETA = false; + // Indicates whether the number of graphs generated during the characterization phase is the default value ot not. + bool CUSTOM_CHARACTERIZATION_NB_GRAPHS = false; + // Using already inferred coordinates. + bool CUSTOM_INFERRED_COORDINATES = false; + // Has a custom name for various output files generated by the program been provided? + bool CUSTOM_OUTPUT_ROOTNAME_MODE = false; + // Has a custom value for the seed been provided? + bool CUSTOM_SEED = false; + // Will the kappas be adjusted after the angular positions inferred? + bool KAPPA_POST_INFERENCE_MODE = true; + // Will or will not position the vertices to maximize the log-likelihood. + bool MAXIMIZATION_MODE = true; + // Does not provide any information during the embedding process. + bool QUIET_MODE = false; + // Refining only the already inferred positions. + bool REFINE_MODE = false; + // Provides various files that characterize the inferred ensemble. + bool VALIDATION_MODE = false; + // Print information about the embedding process on screen instead than on the log file. + bool VERBOSE_MODE = false; + // Allows the program to enter the region beta < 1, where a different connection probability holds + bool ALL_BETA_MODE = true; + // Uses numerical methods to infer mu, instead of the analytic expression that holds for N >> 1 + bool NUMERIC_MU_MODE = true; + + // Global parameters. + public: + // Name of the file containing the previously inferred parameters. + std::string ALREADY_INFERRED_PARAMETERS_FILENAME; + // Minimal/maximal value of beta that the program can handle (bounds). + double BETA_ABS_MAX = 25; + double BETA_ABS_MIN_GEOMETRIC = 1.01; + double BETA_ABS_MIN_NON_GEOMETRIC = 0.01; + // Number of network copies at beta = 0 used to calculate configuration model average local clustering. + int METRICITY_TEST_NB_GRAPHS = 100; + // Number of graphs to generate during the characterization of the inferred ensemble. + int CHARACTERIZATION_NB_GRAPHS = 100; + // Number of new positions to try. + int MIN_NB_ANGLES_TO_TRY = 100; + // // Parameter governing the refinied search for optimal position during the maximization phase. + // int CLOSE_ANGULAR_RANGE_FACTOR = 2; + // Edgelist filename. + std::string EDGELIST_FILENAME; + // Number of points for MC integration in the calculation of expected clustering. + int EXP_CLUST_NB_INTEGRATION_MC_STEPS = 600; + // Number of steps for integration for the expected distance between adjacent vertices. + int EXP_DIST_NB_INTEGRATION_STEPS = 1000; + // Maximal number of attempts to reach convergence of the updated values of kappa. + int KAPPA_MAX_NB_ITER_CONV = 500; + // Maximal number of steps in Newtons Method to find the correct mu. + int FINDING_MU_STEPS = 100; + // // Criterion for the change of the nature of the criterion for the convergence during the + // // maximization of the angular positions. + // int LIMIT_FOR_CONVERGENCE_CRITERION = 625; + // Criterion for changing the calculation of the log-likelihood. + // int NB_VERTICES_IN_CORE = 1000; + // Maximal number of steps in the maximization phase. + // int MAX_NB_ITER_MAXIMIZATION = 15; + // // Maximal angular step when looking for optimal position. + // int MINIMAL_ANGULAR_RESOLUTION = 100; + // Minimal value for the convergence of maximization. + // double MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD = 0.005; + // Minimal values for 2 sigmas when drawing new angles to test. + double MIN_TWO_SIGMAS_NORMAL_DIST = PI / 6; + // Various numerical/convergence thresholds. + double NUMERICAL_CONVERGENCE_THRESHOLD_1 = 1e-3; + double NUMERICAL_CONVERGENCE_THRESHOLD_2 = 5e-5; + double NUMERICAL_CONVERGENCE_THRESHOLD_3 = 0.5; + // double MAXIMIZATION_CONVERGENCE_THRESHOLD = 0.01; + double NUMERICAL_ZERO = 1e-10; + // // Parameter governing the refinied search for optimal position during the maximization phase. + // int REFINED_MAX_STEP_LENGTH_DIVISOR = 5; + // Rootname of output files. + std::string ROOTNAME_OUTPUT; + // Random number generator seed. + int SEED; + private: + // Version of the code. + std::string VERSION = "0.9"; + // Tab. + std::string TAB = " "; + + // General internal objects. + private: + // Random number generator + std::mt19937 engine; + std::uniform_real_distribution uniform_01; + std::normal_distribution normal_01; + // Objects mapping the name and the numerical ID of vertices. + std::map< std::string, int > Name2Num; + std::vector Num2Name; + // List of degree classes. + std::set degree_class; + // Cumulative probability used for the calculation of clustering using MC integration. + std::map< int, std::map< double, int, std::less > > cumul_prob_kgkp; + // List containing the order in which the vertices will be considered in the maximization phase. + std::vector ordered_list_of_vertices; + // Time stamps. + double time0, time1, time2, time3, time4, time5, time6, time7; + time_t time_started, time_ended; + // Stream used to output the log to file. + std::ofstream logfile; + std::streambuf *old_rdbuf; + // Widths of the columns in output file. + int width_names; + int width_values; + + // Objects related to the original graph. + private: + // Number of vertices. + int nb_vertices; + int nb_vertices_degree_gt_one; + // Number of edges. + int nb_edges; + // Average degree. + double average_degree; + // Average local clustering coefficient. + double average_clustering; + // Average degree of neighbors. + std::vector sum_degree_of_neighbors; + // Local clustering. + std::vector nbtriangles; + // Adjacency list. + std::vector< std::set > adjacency_list; + // Degree. + std::vector degree; + // ID of vertices in each degree class. + std::map > degree2vertices; + + // Objects related to the inferred graph ensemble. + public: + // Parameter beta (clustering). + double beta; + private: + // Average degree of the inferred ensemble. + double random_ensemble_average_degree; + // Average local clustering coefficient of the inferred ensemble. + double random_ensemble_average_clustering; + // Maps containing the expected degree of each degree class. + std::map random_ensemble_expected_degree_per_degree_class; + // Expected degrees in the inferred ensemble (analytical, no finite-size effect). + std::vector inferred_ensemble_expected_degree; + // List of kappas by degree class. + std::map random_ensemble_kappa_per_degree_class; + // Parameter mu (average degree). + double mu; + // Hidden variables of the vertices. + std::vector kappa; + // Positions of the vertices. + std::vector theta; + // sinus and cosinus of theta. + // std::vector sin_theta; + // std::vector cos_theta; + + // Objects related to the characterization of the inferred ensemble (by simulations). + private: + // Simulated adjacency list. + std::vector< std::set > simulated_adjacency_list; + // Simulated degrees (one instance). + std::vector simulated_degree; + // Simulated average degree of neighbors (one instance). + std::vector simulated_sum_degree_of_neighbors; + // Simulated clustering coefficients (one instance). + std::vector simulated_nb_triangles; + // Simulated degree distribution (one instance). + std::map simulated_stat_degree; + // Simulated average sum of degree of neighbors by degree class. + std::map simulated_stat_sum_degree_neighbors; + // Simulated average average degree of neighbors by degree class. + std::map simulated_stat_avg_degree_neighbors; + // Simulated average number of triangles gathered by degree class. + std::map simulated_stat_nb_triangles; + // Simulated average clustering coefficient by degree class. + std::map simulated_stat_clustering; + // Characterization of vertices (all instances). + std::vector< std::vector< std::vector > > characterizing_inferred_ensemble_vprops; + // Vertices statistics characterization (all instances). + std::map< int, std::vector > characterizing_inferred_ensemble_vstat; + + // Internal functions. + private: + // === Initialization === + // Extracts all relevant information about the degrees. + void analyze_degrees(); + // Computes the average local clustering coefficient. + void compute_clustering(); + // Loads the graph from an edgelist in a file. + void load_edgelist(); + // Loads the already inferred parameters. + void load_already_inferred_parameters(); + // === Infering parameters === + // Builds the cumulative distribution to choose degree classes in the calculation of clustering. + void build_cumul_dist_for_mc_integration(); + // Computes various properties of the random ensemble (before finding optimal positions). + void rewire_atzero(); + void compute_random_ensemble_average_degree(); + void compute_random_ensemble_clustering(); + double compute_random_ensemble_clustering_for_degree_class(int d1); + // Infers the values of kappa. + void infer_kappas_given_beta_for_all_vertices(); + void infer_kappas_given_beta_for_degree_class(); + // Defines a numerical value for mu, that works for all beta, not just betas away from 1 + void calculate_numerical_mu(); + // === Embedding === + // Computes the log-likelihood between two vertices. + // double compute_pairwise_loglikelihood(int v1, double t1, int v2, double t2); + double compute_pairwise_loglikelihood(int v1, double t1, int v2, double t2, bool neighbors); + // Finds the initial ordering of vertices based on the Eigen Map method. + void find_initial_ordering(std::vector &ordering, std::vector &raw_theta); + void infer_initial_positions(); + // Maximizes the log-likelihood by moving the vertices. + // double find_optimal_angle(int v1); + // double find_approximate_optimal_angle(int v1); + void refine_positions(); + int refine_angle(int v1); + // void refine_angle2(int v1); + // void refine_angle3(int v1); + // void refine_angle4(int v1); + // void refine_angle5(int v1); + // void infer_optimal_positions(); + void finalize(); + // Loads the network and computes topological properties. + void initialize(); + // Infers the parameters (kappas, beta) prior to the embedding. + void infer_parameters(); + // Extracts the onion decomposition (OD) of the graph and orders the vertices according to it. + void order_vertices(); + // Updates the value of the expected degrees given the inferred positions of theta. + void compute_inferred_ensemble_expected_degrees(); + // === Characterization of the inferred ensemble using simulations === + void analyze_simulated_adjacency_list(); + void generate_simulated_adjacency_list(); + // === Miscellaneous === + // Extracts the onion decomposition. + void extract_onion_decomposition(std::vector &coreness, std::vector &od_layer); + // Gets and format current date/time. + std::string format_time(time_t _time); + // Gets the time since epoch in seconds with decimals. + double time_since_epoch_in_seconds(); + // === Output === + // Writes the inferred connection probability into a file. + void save_inferred_connection_probability(); + // Writes the inferred properties of vertices into a file. + void save_inferred_ensemble_characterization(); + // Writes the inferred theta density into a file. + void save_inferred_theta_density(); + // Writes the inferred info into a file. + void save_inferred_coordinates(); + // Function associated to the extraction of the components. + int get_root(int i, std::vector &clust_id); + void merge_clusters(std::vector &size, std::vector &clust_id); + void check_connected_components(); + + // Public functions to perform the embeddings. + public: + // Constructor (empty). + embeddingS1_t() {}; + // Destructor (empty). + ~embeddingS1_t() {}; + // Performs the embedding. + void embed(); + void embed(std::string edgelist_filename) { EDGELIST_FILENAME = edgelist_filename; embed(); }; +}; + + + + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::analyze_degrees() +{ + // Resets the value of the average degree. + average_degree = 0; + // Resets the number of vertices with a degree greater than 1. + nb_vertices_degree_gt_one = 0; + // Resizes the list of degrees. + degree.clear(); + degree.resize(nb_vertices); + if(VALIDATION_MODE) + { + sum_degree_of_neighbors.clear(); + sum_degree_of_neighbors.resize(nb_vertices, 0); + } + // Populates the list of degrees, the average degree and + // the list of vertices of each degree class. + std::set::iterator it, end; + for(int n(0), k; n 1) + { + ++nb_vertices_degree_gt_one; + } + if(VALIDATION_MODE) + { + it = adjacency_list[n].begin(); + end = adjacency_list[n].end(); + for(; it!=end; ++it) + { + sum_degree_of_neighbors[*it] += k; + } + } + } + // Completes the computation of the average degree. + average_degree /= nb_vertices; +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::analyze_simulated_adjacency_list() +{ + // Initializes the containers. + simulated_degree.clear(); + simulated_degree.resize(nb_vertices); + simulated_sum_degree_of_neighbors.clear(); + simulated_sum_degree_of_neighbors.resize(nb_vertices, 0); + simulated_nb_triangles.clear(); + simulated_nb_triangles.resize(nb_vertices, 0); + simulated_stat_degree.clear(); + simulated_stat_sum_degree_neighbors.clear(); + simulated_stat_avg_degree_neighbors.clear(); + simulated_stat_nb_triangles.clear(); + simulated_stat_clustering.clear(); + // Analyzes the degree and the average degree of neighbors. + std::set::iterator it, end; + for(int v1(0), d1; v1 intersection; + // Set objects. + std::set neighbors_v2; + // Iterator objects. + std::set::iterator it1, end1, it2, end2; + std::map >::iterator it3, end3; + std::vector::iterator it4; + // Computes the intersection for the in- and out- neighbourhoods of each vertex. + for(int v1(0), d1; v1 1. + d1 = simulated_degree[v1]; + if( d1 > 1 ) + { + // Loops over the neighbors of vertex v1. + it1 = simulated_adjacency_list[v1].begin(); + end1 = simulated_adjacency_list[v1].end(); + for(; it1!=end1; ++it1) + { + // Performs the calculation only if degree > 1. + if( simulated_degree[*it1] > 1 ) + { + // Builds an ordered list of the neighbourhood of v2 + it2 = simulated_adjacency_list[*it1].begin(); + end2 = simulated_adjacency_list[*it1].end(); + neighbors_v2.clear(); + for(; it2!=end2; ++it2) + { + if(*it1 < *it2) // Ensures that triangles will be counted only once. + { + neighbors_v2.insert(*it2); + } + } + // Counts the number of triangles. + if(neighbors_v2.size() > 0) + { + intersection.clear(); + intersection.resize(std::min(simulated_adjacency_list[v1].size(), neighbors_v2.size())); + it4 = std::set_intersection(simulated_adjacency_list[v1].begin(), simulated_adjacency_list[v1].end(), + neighbors_v2.begin(), neighbors_v2.end(), intersection.begin()); + intersection.resize(it4-intersection.begin()); + nb_triangles += intersection.size(); + } + } + } + // Adds the contribution of vertex v1 to the average clustering coefficient. + // tmp_val = 2 * nb_triangles / (d1 * (d1 - 1)); + simulated_nb_triangles[v1] = nb_triangles; + // simulated_nb_triangles[v1] = tmp_val; + } + } + // + for(int v1(0), d1; v1 0) + { + simulated_stat_sum_degree_neighbors[d1] += simulated_sum_degree_of_neighbors[v1]; + simulated_stat_avg_degree_neighbors[d1] += simulated_sum_degree_of_neighbors[v1] / d1; + } + if(d1 > 1) + { + simulated_stat_nb_triangles[d1] += simulated_nb_triangles[v1]; + simulated_stat_clustering[d1] += 2 * simulated_nb_triangles[v1] / d1 / (d1 - 1); + } + } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::build_cumul_dist_for_mc_integration() +{ + // Variables. + int v1; + double tmp_val, tmp_cumul; + // Parameters. + double R = nb_vertices / (2 * PI); + if(!NUMERIC_MU_MODE && beta >=1){mu = beta * std::sin(PI / beta) / (2.0 * PI * average_degree);} + else if(!NUMERIC_MU_MODE && beta < 1){mu = (1 - beta) * std::pow(2, -beta) * std::pow(nb_vertices, beta - 1) / average_degree;} + else {calculate_numerical_mu();} + // Temporary container. + std::map nkkp; + // Resets the main object. + cumul_prob_kgkp.clear(); + // Iterator objects. + std::set::iterator it1, end1, it2, end2; + // For all degree classes over 1. + it1 = degree_class.begin(); + end1 = degree_class.end(); + while(*it1 < 2) { ++it1; } + for(; it1!=end1; ++it1) + { + // Reinitializes the temporary container. + nkkp.clear(); + + // For all degree classes. + it2 = degree_class.begin(); + end2 = degree_class.end(); + for(; it2!=end2; ++it2) + { + // Initializes the temporary container. + nkkp[*it2] = 0; + } + + // For all degree classes. + it2 = degree_class.begin(); + end2 = degree_class.end(); + for(; it2!=end2; ++it2) + { + tmp_val = hyp2f1a(beta, PI * R , mu * random_ensemble_kappa_per_degree_class[*it1] * random_ensemble_kappa_per_degree_class[*it2]); + nkkp[*it2] = degree2vertices[*it2].size() * tmp_val / random_ensemble_expected_degree_per_degree_class[*it1]; + } + + // Initializes the cumulating variable. + tmp_cumul = 0; + // Initializes the sub-container. + cumul_prob_kgkp[*it1]; + // For all degree classes. + it2 = degree_class.begin(); + end2 = degree_class.end(); + for(; it2!=end2; ++it2) + { + tmp_val = nkkp[*it2]; + if(tmp_val > NUMERICAL_ZERO) + { + // Cumulates the probabilities; + tmp_cumul += tmp_val; + // Builds the cumulative distribution. + cumul_prob_kgkp[*it1][tmp_cumul] = *it2; + } + } + } +} + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::rewire_atzero() +{ + // For rewiring + // Variables. + long int counter(1); + long int maxcounter(10*METRICITY_TEST_NB_GRAPHS*nb_edges); + int nodei,nodej,nodel,nodem; + int randomij,randomlm; + // Vector objects. + std::vector> edges(nb_edges); + std::vector> rewired_adjacency_list(nb_vertices); + std::set> edges_set; + // Set objects. + std::set>::iterator edge_it; + std::set link; + + //For calculating clustering + // Variables. + int links(0); + double rewired_average_clustering; + double mu(0), std(0), Pbis0; + // Vector objects. + vector clustering_realizations; + + //Loops over all the vertices. + for (int i = 0; i < adjacency_list.size(); i++) + { + //Loops over the neighbours of vertex i. + for (std::set::iterator it = adjacency_list.at(i).begin(); it != adjacency_list.at(i).end(); ++it) + { + //Defines a link as the set [i,*it]. + link.insert(i); + link.insert(*it); + //Inserts the link into the edge list. Double links are avoided because of c++ set. + edges_set.insert(link); + //Clear the link + link.clear(); + //Insert the neighbour into a copy of the adjacency list, using as a container a c++ vector, such that the elements can be changed during rewiring. + rewired_adjacency_list.at(i).push_back(*it); + } + } + + //Copy the content of the edge list into a c++ vector container so that elements can be changed during rewiring. + edge_it = edges_set.begin(); + for (int i = 0; i < nb_edges; i++){ + link = *edge_it; + //This step is necessary to destroy any correlations between the index name and its position in the edge pair. + if (uniform_01(engine)<0.5) + { + edges.at(i).push_back(*link.begin()); + edges.at(i).push_back(*link.rbegin()); + } + else + { + edges.at(i).push_back(*link.rbegin()); + edges.at(i).push_back(*link.begin()); + } + ++edge_it; + } + + //rewire until maxcounter is reached. + while (counter <= maxcounter) + { + //initialize the 4 nodes that are effected by the rewiring step. Choose them the same such that the while loop is entered. + nodei = nodej = nodel = nodem = 0; + + //Choose new candidates if: + //-Any two nodes are identical. + //-node i and m are already connected. + //-node j and l are already connected. + while(nodei==nodej || nodei == nodel || nodei == nodem || nodej == nodem || nodej == nodel || nodel == nodem || + std::find(rewired_adjacency_list.at(nodei).begin(),rewired_adjacency_list.at(nodei).end(),nodem)!=rewired_adjacency_list.at(nodei).end() || + std::find(rewired_adjacency_list.at(nodel).begin(),rewired_adjacency_list.at(nodel).end(),nodej)!=rewired_adjacency_list.at(nodel).end()) + { + //choose two random edges. + randomij = std::floor(uniform_01(engine)*nb_edges); + randomlm = std::floor(uniform_01(engine)*nb_edges); + + //node i and j are the two nodes connected by the first edge and node l and m by the second. + nodei = edges.at(randomij).at(0); + nodej = edges.at(randomij).at(1); + nodel = edges.at(randomlm).at(0); + nodem = edges.at(randomlm).at(1); + } + + //once a set of nodes has been accepted, perform the rewiring. Node i connects to m and l to j. + edges.at(randomij).at(1) = nodem; + edges.at(randomlm).at(1) = nodej; + + *(std::find(rewired_adjacency_list.at(nodei).begin(),rewired_adjacency_list.at(nodei).end(),nodej)) = nodem; + *(std::find(rewired_adjacency_list.at(nodej).begin(),rewired_adjacency_list.at(nodej).end(),nodei)) = nodel; + *(std::find(rewired_adjacency_list.at(nodem).begin(),rewired_adjacency_list.at(nodem).end(),nodel)) = nodei; + *(std::find(rewired_adjacency_list.at(nodel).begin(),rewired_adjacency_list.at(nodel).end(),nodem)) = nodej; + + //Perform 10*nb_edges rewirings to destroy all correlations between the networks in the monte carlo sampling scheme. According to https://doi.org/10.1093/comnet/cnu041, this should be enough. + if (counter % (10*nb_edges) == 0) + { + //Calculate the average local clustering coefficient + rewired_average_clustering = 0; + for(int i = 0; i 1){ + for (vector::iterator it1 = rewired_adjacency_list.at(i).begin(); it1 != rewired_adjacency_list.at(i).end(); ++it1){ + for (vector::iterator it2 = (it1+1); it2 != rewired_adjacency_list.at(i).end(); ++it2){ + for (vector::iterator it3 = rewired_adjacency_list.at(*it2).begin(); it3 != rewired_adjacency_list.at(*it2).end(); ++it3){ + if (*it1 == *it3){links ++;} + } + } + } + rewired_average_clustering += 2 * links * 1.0 / (degree.at(i)*(degree.at(i)-1)); + } + links = 0; + } + clustering_realizations.push_back(rewired_average_clustering/nb_vertices_degree_gt_one); + } + + counter ++; + + } + + //calculate average clustering + for (std::vector::iterator it = clustering_realizations.begin(); it != clustering_realizations.end(); ++it){ + mu += *it / METRICITY_TEST_NB_GRAPHS; + } + + //calculate standard deviation of clustering + for (std::vector::iterator it = clustering_realizations.begin(); it != clustering_realizations.end(); ++it){ + std += (*it - mu)*(*it - mu) / (METRICITY_TEST_NB_GRAPHS - 1); + } + std = std::sqrt(std); + + //Assuming a gaussian distribution, calculate the complementary cumulative distribution function + Pbis0 = 0.5 *( 1 - std::erf((average_clustering-mu)/(std*std::sqrt(2)))); + + if (Pbis0 < 0.01/*(average_clustering - mu)/std > 3*/) + { + if(!QUIET_MODE) { std::clog << "Metricity likely, P(C > c | beta = 0) = " << Pbis0 << std::endl;}; + } + else + { + if(!QUIET_MODE) { std::clog << "WARNING: EXTREMELY WEAK METRICITY, P(C > c | beta = 0) = " << Pbis0 << std::endl;}; + std::cerr << "Extremely weak geometry, P(C > c | beta = 0) = " << Pbis0 << std::endl; + std::cerr << "Mercator unable to determine beta. Please rerun the program with a manually chosen beta." << std::endl; + std::cerr << std::endl; + std::terminate(); + } +} + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::compute_clustering() +{ + // Variables. + double nb_triangles, tmp_val; + // Vector objects. + std::vector intersection; + // Set objects. + std::set neighbors_v2; + // Iterator objects. + std::vector::iterator it; + std::set::iterator it1, end1, it2, end2; + std::map >::iterator it3, end3; + if(VALIDATION_MODE) + { + // Initializes the individual clustering coefficients. + nbtriangles.clear(); + nbtriangles.resize(nb_vertices, 0); + } + // Computes the intersection for the in- and out- neighbourhoods of each vertex. + for(int v1(0), d1; v1 1. + d1 = degree[v1]; + if( d1 > 1 ) + { + // Loops over the neighbors of vertex v1. + it1 = adjacency_list[v1].begin(); + end1 = adjacency_list[v1].end(); + for(; it1!=end1; ++it1) + { + // Performs the calculation only if degree > 1. + if( degree[*it1] > 1 ) + { + // Builds an ordered list of the neighbourhood of v2 + it2 = adjacency_list[*it1].begin(); + end2 = adjacency_list[*it1].end(); + neighbors_v2.clear(); + for(; it2!=end2; ++it2) + { + if(*it1 < *it2) // Ensures that triangles will be counted only once. + { + neighbors_v2.insert(*it2); + } + } + // Counts the number of triangles. + if(neighbors_v2.size() > 0) + { + intersection.clear(); + intersection.resize(std::min(adjacency_list[v1].size(), neighbors_v2.size())); + it = std::set_intersection(adjacency_list[v1].begin(), adjacency_list[v1].end(), + neighbors_v2.begin(), neighbors_v2.end(), intersection.begin()); + intersection.resize(it-intersection.begin()); + nb_triangles += intersection.size(); + } + } + } + // Adds the contribution of vertex v1 to the average clustering coefficient. + tmp_val = 2 * nb_triangles / (d1 * (d1 - 1)); + average_clustering += tmp_val; + if(VALIDATION_MODE) + { + nbtriangles[v1] = nb_triangles; + } + } + } + // Completes the calculation of the average local clustering coefficient. + average_clustering /= nb_vertices_degree_gt_one; +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::compute_inferred_ensemble_expected_degrees() +{ + // Variables. + double kappa1, theta1, dtheta, prob; + // Computes the new expected degrees given the inferred values of theta. + inferred_ensemble_expected_degree.clear(); + inferred_ensemble_expected_degree.resize(nb_vertices, 0); + for(int v1(0); v1=1){prob = 1 / (1 + std::pow((nb_vertices * dtheta) / (2 * PI * mu * kappa1 * kappa[v2]), beta));} + if(beta< 1){prob = 1 / (1 + std::pow((nb_vertices * dtheta) / (2 * PI), beta) * 1.0 / (mu * kappa1 * kappa[v2]));} + inferred_ensemble_expected_degree[v1] += prob; + inferred_ensemble_expected_degree[v2] += prob; + } + } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::compute_random_ensemble_average_degree() +{ + // Computes the ensemble average degree. + random_ensemble_average_degree = 0; + std::map::iterator it2 = random_ensemble_expected_degree_per_degree_class.begin(); + std::map::iterator end2 = random_ensemble_expected_degree_per_degree_class.end(); + + for(; it2!=end2; ++it2) + { + random_ensemble_average_degree += it2->second * degree2vertices[it2->first].size(); + } + random_ensemble_average_degree /= nb_vertices; +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::compute_random_ensemble_clustering() +{ + // Reinitializes the average clustering for the ensemble. + random_ensemble_average_clustering = 0; + // Computes the inferred ensemble clustering spectrum for all degree classes over 1. + std::set::iterator it = degree_class.begin(); + std::set::iterator end = degree_class.end(); + while(*it < 2) { ++it; } + for(double p23; it!=end; ++it) + { + // Computes the clustering coefficient for the degree class and updates the average value. + p23 = compute_random_ensemble_clustering_for_degree_class(*it); + // ensemble_clustering_spectrum[*it] = p23; + random_ensemble_average_clustering += p23 * degree2vertices[*it].size(); + } + // Completes the calculation of the average clustering coefficient of the inferred ensemble. + random_ensemble_average_clustering /= nb_vertices_degree_gt_one; +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +double embeddingS1_t::compute_random_ensemble_clustering_for_degree_class(int d1) +{ + // Variables. + int d2, d3; + double p12, p13, pc, pz, zmin, zmax, z, z12, z13, da, tmp; + double p23 = 0; + // Parameters. + int nb_points = EXP_CLUST_NB_INTEGRATION_MC_STEPS; + double R = nb_vertices / (2 * PI); + if(!NUMERIC_MU_MODE && beta > 1){mu = beta * std::sin(PI / beta) / (2.0 * PI * average_degree);} + else if(!NUMERIC_MU_MODE && beta < 1){mu = (1 - beta) * std::pow(2, -beta) * std::pow(nb_vertices, beta - 1) / average_degree;} + else if(!NUMERIC_MU_MODE && beta == 1){mu = 1.0 / (2 * average_degree * std::log(nb_vertices));} + else {calculate_numerical_mu();} + // MC integration. + for(int i(0); isecond; + // Computes their probability of being connected. + p12 = hyp2f1a(beta, PI * R, mu * random_ensemble_kappa_per_degree_class[d1] * random_ensemble_kappa_per_degree_class[d2]); + + // Gets the degree of vertex 3; + d3 = cumul_prob_kgkp[d1].lower_bound(uniform_01(engine))->second; + // Computes their probability of being connected. + p13 = hyp2f1a(beta, PI * R, mu * random_ensemble_kappa_per_degree_class[d1] * random_ensemble_kappa_per_degree_class[d3]); + + pc = uniform_01(engine); + zmin = 0; + zmax = PI; + while( (zmax - zmin) > NUMERICAL_CONVERGENCE_THRESHOLD_2 ) + { + z = (zmax + zmin) / 2; + pz = (z / PI) * hyp2f1a(beta, z * R, mu * random_ensemble_kappa_per_degree_class[d1] * random_ensemble_kappa_per_degree_class[d2]) / p12; + if(pz > pc) + { + zmax = z; + } + else + { + zmin = z; + } + } + z12 = (zmax + zmin) / 2; + + // + pc = uniform_01(engine); + zmin = 0; + zmax = PI; + while( (zmax - zmin) > NUMERICAL_CONVERGENCE_THRESHOLD_2 ) + { + z = (zmax + zmin) / 2; + pz = (z / PI) * hyp2f1a(beta, z * R, mu * random_ensemble_kappa_per_degree_class[d1] * random_ensemble_kappa_per_degree_class[d3]) / p13; + if(pz > pc) + { + zmax = z; + } + else + { + zmin = z; + } + } + z13 = (zmax + zmin) / 2; + + + // + if(uniform_01(engine) < 0.5) + { + da = std::fabs(z12 + z13); + } + else + { + da = std::fabs(z12 - z13); + } + da = std::min(da, (2.0 * PI) - da); + if(da < NUMERICAL_ZERO) + { + p23 += 1; + } + else + { + if(beta>=1){p23 += 1.0 / (1.0 + std::pow((da * R) / (mu * random_ensemble_kappa_per_degree_class[d2] * random_ensemble_kappa_per_degree_class[d3]), beta));} + if(beta< 1){p23 += 1.0 / (1.0 + std::pow((da * R), beta) * 1.0 / (mu * random_ensemble_kappa_per_degree_class[d2] * random_ensemble_kappa_per_degree_class[d3]));} + } + } + // Returns the value of the local clustering coefficient for this degree class. + return p23 / nb_points; +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +double embeddingS1_t::compute_pairwise_loglikelihood(int v1, double t1, int v2, double t2, bool neighbors) +{ + // Avoids to compute the pairwise loglikelihood of the vertex with itself. + if(v1 == v2) + { + return 0; + } + // Computes the angular separation. + double da = PI - std::fabs(PI - std::fabs(t1 - t2)); + // Computes the loglikelihood between vertives v1 and v2 according to whether they are neighbors or not. + if(neighbors) + { + if(beta>=1){return -1 * beta * std::log( (nb_vertices * da) / (2 * PI * mu * kappa[v1] * kappa[v2]) );} + else{return -1 * beta * std::log( (nb_vertices * da) / (2 * PI) ) + std::log(mu * kappa[v1] * kappa[v2]) ;} + } + else // not neighbors + { + if(beta>=1){return -1 * std::log( 1 + std::pow( (nb_vertices * da) / (2 * PI * mu * kappa[v1] * kappa[v2]), -beta) );} + else{return -1 * std::log( 1 + std::pow( (nb_vertices * da) / (2 * PI), - beta) * mu * kappa[v1] * kappa[v2]);} + } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::embed() +{ + // Gets current time. + time0 = time_since_epoch_in_seconds(); + time_started = std::time(NULL); + // Initialization. + initialize(); + // Gets current time. + time1 = time_since_epoch_in_seconds(); + if(REFINE_MODE) + { + // Loads the parameters from the .inf_coord file. + load_already_inferred_parameters(); + } + if(!REFINE_MODE) + { + // Pre-processing: infers the parameters used for the embedding. + infer_parameters(); + } + // Gets current time. + time2 = time_since_epoch_in_seconds(); + if(!REFINE_MODE) + { + // First phase: educated guess of the positions. + infer_initial_positions(); + } + // Gets current time. + time3 = time_since_epoch_in_seconds(); + if(MAXIMIZATION_MODE) + { + // Second phase: likelihood maximization. + // infer_optimal_positions(); + refine_positions(); + } + // Gets current time. + time4 = time_since_epoch_in_seconds(); + if(KAPPA_POST_INFERENCE_MODE) + { + // Post-processing: adjusts the values of kappa based on the inferred positions. + infer_kappas_given_beta_for_all_vertices(); + } + // Gets the current time. + time5 = time_since_epoch_in_seconds(); + time_ended = std::time(NULL); + // Saves the inferred coordinates in a file. + save_inferred_coordinates(); + if(VALIDATION_MODE) + { + save_inferred_theta_density(); + save_inferred_connection_probability(); + } + // Gets the current time. + time6 = time_since_epoch_in_seconds(); + if(CHARACTERIZATION_MODE) + { + save_inferred_ensemble_characterization(); + } + // Gets the current time. + time7 = time_since_epoch_in_seconds(); + time_ended = std::time(NULL); + // + finalize(); +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::extract_onion_decomposition(std::vector &coreness, std::vector &od_layer) +{ + // Builds two lists (std::vector, std::set) of the degree of the vertices. + std::vector DegreeVec(nb_vertices); + std::set > DegreeSet; + for(int v(0); v::iterator it1, end; + std::set< std::pair > LayerSet; + // std::set< std::pair > order_in_layer; + std::set< std::pair >::iterator m_it; + // std::set< std::pair >::iterator o_it, o_end; + while(DegreeSet.size() > 0) + { + // Populates the set containing the vertices belonging to the same layer. + m_it = DegreeSet.begin(); + d1 = m_it->first; + // Increases the layer id. + current_layer += 1; + // Sets the coreness and the layer the vertices with the same degree. + while(m_it->first == d1 && m_it != DegreeSet.end()) + { + // Sets the coreness and the layer. + v1 = m_it->second; + coreness[v1] = d1; + od_layer[v1] = current_layer; + // Looks at the next vertex. + ++m_it; + } + // Adds the vertices of the layer to the set. + LayerSet.insert(DegreeSet.begin(), m_it); + // Removes the vertices of the current layer. + DegreeSet.erase(DegreeSet.begin(), m_it); + // Modifies the "effective" degree of the neighbors of the vertices in the layer. + while(LayerSet.size() > 0) + { + // Gets information about the next vertex of the layer. + v1 = LayerSet.begin()->second; + // Reduces the "effective" degree of its neighbours. + it1 = adjacency_list[v1].begin(); + end = adjacency_list[v1].end(); + for(; it1!=end; ++it1) + { + // Identifies the neighbor. + v2 = *it1; + d2 = DegreeVec[v2]; + // Finds the neighbor in the list "effective" degrees. + m_it = DegreeSet.find(std::make_pair(d2, v2)); + if(m_it != DegreeSet.end()) + { + if(d2 > d1) + { + DegreeVec[v2] = d2 - 1; + DegreeSet.erase(m_it); + DegreeSet.insert(std::make_pair(d2 - 1, v2)); + } + } + } + // Removes the vertices from the LayerSet. + LayerSet.erase(LayerSet.begin()); + } + } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::finalize() +{ + // Resets the formating of std::clog. + std::clog << std::resetiosflags(std::ios::floatfield | std::ios::fixed | std::ios::showpoint); + // Writes the parameters used in the log for reproductibility. + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "Internal parameters and options" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "ALREADY_INFERRED_PARAMETERS_FILENAME " << ALREADY_INFERRED_PARAMETERS_FILENAME << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "BETA_ABS_MAX " << BETA_ABS_MAX << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "BETA_ABS_MIN_GEOMETRIC " << BETA_ABS_MIN_GEOMETRIC << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "BETA_ABS_MIN_NON_GEOMETRIC " << BETA_ABS_MIN_NON_GEOMETRIC << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "CHARACTERIZATION_MODE " << (CHARACTERIZATION_MODE ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "CHARACTERIZATION_NB_GRAPHS " << CHARACTERIZATION_NB_GRAPHS << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "CLEAN_RAW_OUTPUT_MODE " << (CLEAN_RAW_OUTPUT_MODE ? "true" : "false") << std::endl; } + // if(!QUIET_MODE) { std::clog << TAB << "CLOSE_ANGULAR_RANGE_FACTOR " << CLOSE_ANGULAR_RANGE_FACTOR << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_BETA " << (CUSTOM_BETA ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_CHARACTERIZATION_NB_GRAPHS " << (CUSTOM_CHARACTERIZATION_NB_GRAPHS ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_INFERRED_COORDINATES " << (CUSTOM_INFERRED_COORDINATES ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_OUTPUT_ROOTNAME_MODE " << (CUSTOM_OUTPUT_ROOTNAME_MODE ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "CUSTOM_SEED " << (CUSTOM_SEED ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "EDGELIST_FILENAME: " << EDGELIST_FILENAME << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "EXP_CLUST_NB_INTEGRATION_MC_STEPS " << EXP_CLUST_NB_INTEGRATION_MC_STEPS << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "EXP_DIST_NB_INTEGRATION_STEPS " << EXP_DIST_NB_INTEGRATION_STEPS << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "FINDING_MU_STEPS " << FINDING_MU_STEPS << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "KAPPA_MAX_NB_ITER_CONV " << KAPPA_MAX_NB_ITER_CONV << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "KAPPA_POST_INFERENCE_MODE " << (KAPPA_POST_INFERENCE_MODE ? "true" : "false") << std::endl; } + // if(!QUIET_MODE) { std::clog << TAB << "LIMIT_FOR_CONVERGENCE_CRITERION " << LIMIT_FOR_CONVERGENCE_CRITERION << std::endl; } + // if(!QUIET_MODE) { std::clog << TAB << "MAX_NB_ITER_MAXIMIZATION " << MAX_NB_ITER_MAXIMIZATION << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "MAXIMIZATION_MODE " << (MAXIMIZATION_MODE ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "METRICITY_TEST_NB_GRAPHS " << METRICITY_TEST_NB_GRAPHS << std::endl; } + // if(!QUIET_MODE) { std::clog << TAB << "MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD " << MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD << std::endl; } + // if(!QUIET_MODE) { std::clog << TAB << "MINIMAL_ANGULAR_RESOLUTION " << MINIMAL_ANGULAR_RESOLUTION << std::endl; } + // if(!QUIET_MODE) { std::clog << TAB << "NB_VERTICES_IN_CORE " << NB_VERTICES_IN_CORE << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "MIN_NB_ANGLES_TO_TRY " << MIN_NB_ANGLES_TO_TRY << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_1 " << NUMERICAL_CONVERGENCE_THRESHOLD_1 << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_2 " << NUMERICAL_CONVERGENCE_THRESHOLD_2 << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_3 " << NUMERICAL_CONVERGENCE_THRESHOLD_3 << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "NUMERICAL_ZERO " << NUMERICAL_ZERO << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "QUIET_MODE " << (QUIET_MODE ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "REFINE_MODE " << (REFINE_MODE ? "true" : "false") << std::endl; } + // if(!QUIET_MODE) { std::clog << TAB << "REFINED_MAX_STEP_LENGTH_DIVISOR " << REFINED_MAX_STEP_LENGTH_DIVISOR << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "ROOTNAME_OUTPUT: " << ROOTNAME_OUTPUT << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "SEED " << SEED << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "VALIDATION_MODE " << (VALIDATION_MODE ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "ALL_BETA_MODE " << (ALL_BETA_MODE ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "NUMERIC_MU_MODE " << (NUMERIC_MU_MODE ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "VERBOSE_MODE " << (VERBOSE_MODE ? "true" : "false") << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "VERSION " << VERSION << std::endl; } + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "Ended on: " << format_time(time_ended) << std::endl; } + if(!QUIET_MODE) { std::clog << "Elapsed CPU time (embedding): " << std::setw(10) << std::fixed << time5 - time0 << " seconds" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "initialization: " << std::setw(10) << std::fixed << time1 - time0 << " seconds" << std::endl; } + if(!REFINE_MODE) + if(!QUIET_MODE) { std::clog << TAB << "parameters inference: " << std::setw(10) << std::fixed << time2 - time1 << " seconds" << std::endl; } + if(!REFINE_MODE) + if(!QUIET_MODE) { std::clog << TAB << "initial positions: " << std::setw(10) << std::fixed << time3 - time2 << " seconds" << std::endl; } + if(REFINE_MODE) + if(!QUIET_MODE) { std::clog << TAB << "loading previous positions: " << std::setw(10) << std::fixed << time3 - time1 << " seconds" << std::endl; } + if(MAXIMIZATION_MODE) + if(!QUIET_MODE) { std::clog << TAB << "refining positions: " << std::setw(10) << std::fixed << time4 - time3 << " seconds" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "adjusting kappas: " << std::setw(10) << std::fixed << time5 - time4 << " seconds" << std::endl; } + if(VALIDATION_MODE || CHARACTERIZATION_MODE) + if(!QUIET_MODE) { std::clog << "Elapsed CPU time (validation): " << std::setw(10) << std::fixed << time7 - time5 << " seconds" << std::endl; } + if(VALIDATION_MODE) + if(!QUIET_MODE) { std::clog << TAB << "validating embedding: " << std::setw(10) << std::fixed << time6 - time5 << " seconds" << std::endl; } + if(CHARACTERIZATION_MODE) + if(!QUIET_MODE) { std::clog << TAB << "characterizing ensemble: " << std::setw(10) << std::fixed << time7 - time6 << " seconds" << std::endl; } + if(!QUIET_MODE) { std::clog << "===========================================================================================" << std::endl; } + // Puts the streams back into place (to avoid a segmentation fault when exiting program). + if(!QUIET_MODE) + { + if(!VERBOSE_MODE) + { + logfile.close(); + // Reset the rdbuf of clog. + std::clog.rdbuf(old_rdbuf); + } + } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::find_initial_ordering(std::vector &ordering, std::vector &raw_theta) +{ + if(!QUIET_MODE) { std::clog << std::endl << TAB << "Building the weights matrix..."; } + // Initializes the sparse matrix. + Eigen::SparseMatrix L(nb_vertices_degree_gt_one, nb_vertices_degree_gt_one); + L.reserve(Eigen::VectorXi::Constant(nb_vertices_degree_gt_one, 3)); + // ASSUMES THAT THERE IS ONLY ONE CONNECTED COMPONENT. + + // Sets contiguous IDs excluding vertices with degree 1. + std::vector newID(nb_vertices, -1); + int n(0); + for(int v(0); v 1) + { + newID[v] = n; + ++n; + } + } + if(n != nb_vertices_degree_gt_one) + std::cout << "There is something wrong here." << std::endl; + + // First, fills the matrix with the expected distance between connected vertices according to S1. + double k1, k2, expected_distance, val, t(0), norm(0); + std::set::iterator it, end; + for(int v1(0), v2, d1, d2, n1, n2; v1 PI) + { + // Just a verification. + std::cerr << "Warning. Expected angular distance out of range." << std::endl; + std::terminate(); + } + // Transforms the arc length distance into Euclidean distances (unit circle). + expected_distance = 2 * std::sin(expected_distance / 2); + // Fills the matrix. + L.insert(n1, n2) = expected_distance; + L.insert(n2, n1) = expected_distance; + // Scaling factor of the weights (see below). + t += 2 * expected_distance * expected_distance; + norm += 2; + } + } + } + } + } + t /= norm; + // Second, takes the appropriate scaled exponential of each entry and computes the "strengths". + double value; + std::vector strength(nb_vertices_degree_gt_one, 0); + for(int k(0), kk(L.outerSize()), v1, v2; k::InnerIterator it(L, k); it; ++it) + { + v1 = it.row(); + v2 = it.col(); + value = it.value(); + value = std::exp(-1 * value * value / t); + L.coeffRef(v1, v2) = value; + strength[v1] += value; + } + } + // Third, multiply by the left with the inverse matrix of the strengths to tranform the + // generalized eigenvalue problem into a "regular" eigenvalue problem. + for(int k(0), kk(L.outerSize()), v1, v2; k::InnerIterator it(L, k); it; ++it) + { + v1 = it.row(); + v2 = it.col(); + value = it.value(); + L.coeffRef(v1, v2) = -1 * value / strength[v1]; + } + } + // Fourth, fills the diagonal. + for(int v1(0); v1 op(L); + // Containers for the eigenvectors. + Eigen::MatrixXcd evectors; + // Convergence parameter. + int ncv = 7; + // Initializes and computes. + // if(!QUIET_MODE) { std::clog << std::endl << TAB << "Using Spectra parameter ncv = " << ncv << " to compute eigenvectors...";} + // eigs.init(); + // int nconv = eigs.compute(); + // Flag indicating whether another iteration is required. + if(!QUIET_MODE) { std::clog << std::endl; } + bool keep_going = true; + while(keep_going) + { + // // Checks for errors. + // if(eigs.info() != Spectra::SUCCESSFUL) + // { + // if(eigs.info() == Spectra::NOT_COMPUTED) + // std::cerr << "NOT_COMPUTED" << std::endl; + // if(eigs.info() == Spectra::NOT_CONVERGING) + // std::cerr << "NOT_CONVERGING" << std::endl; + // if(eigs.info() == Spectra::NUMERICAL_ISSUE) + // std::cerr << "NUMERICAL_ISSUE" << std::endl; + // std::terminate(); + // } + // Constructs eigen solver object. + Spectra::GenEigsSolver< double, Spectra::SMALLEST_MAGN, Spectra::SparseGenMatProd > eigs(&op, 3, ncv); + // Initializes and computes. + if(!QUIET_MODE) { std::clog << TAB << "Computing eigenvectors using Spectra parameter ncv = " << ncv << "..."; } + eigs.init(); + int nconv = eigs.compute(); + // Retrieves the eigenvectors. + evectors = eigs.eigenvectors(); + + // Checks whether another iteration is required. + if(eigs.info() != Spectra::SUCCESSFUL) + { + // Checks that the convergence parameter has not reached its maximal value. + if(ncv == nb_vertices) + { + std::cerr << std::endl << "The algorithm computing the eigenvectors (Spectra library) cannot converge at all... Exiting." << std::endl << std::endl; + std::terminate(); + } + if(!QUIET_MODE) { std::clog << " Convergence not reached." << std::endl; } + // Increases the convergence parameter. + ncv = std::pow(ncv, 1.5); + if(ncv > nb_vertices) + { + ncv = nb_vertices; + } + } + else + { + keep_going = false; + } + } + if(!QUIET_MODE) { std::clog << " Convergence reached." << std::endl; } + + /*TEST*/// Initializes the container for the raw angular positions. + /*TEST*/raw_theta.clear(); + /*TEST*/raw_theta.resize(nb_vertices); + // Orders the vertices. + double angle; + std::set< std::pair > ordering_set; + ordering_set.clear(); + for(int v1(0), n1; v1 list_neigh_degree_one; + std::set< std::pair >::iterator it2 = ordering_set.begin(); + std::set< std::pair >::iterator end2 = ordering_set.end(); + for(int v1; it2!=end2; ++it2) + { + // Gets the identity of the vertex. + v1 = it2->second; + // Counts the number of neighbors with degree 1 + list_neigh_degree_one.clear(); + list_neigh_degree_one.reserve(degree[v1]); + it = adjacency_list[v1].begin(); + end = adjacency_list[v1].end(); + for(int v2; it!=end; ++it) + { + v2 = *it; + if(degree[v2] == 1) + { + list_neigh_degree_one.push_back(v2); + } + } + // Adds the neighbors of degree 1 around the vertex in the ordering. + for(int v(0), vv(list_neigh_degree_one.size() / 2); vtm_year + 1900; + int month = aTime->tm_mon + 1; + int day = aTime->tm_mday; + int hours = aTime->tm_hour; + int minutes = aTime->tm_min; + // Format the string. + std::string the_time = std::to_string(year) + "/"; + if(month < 10) + the_time += "0"; + the_time += std::to_string(month) + "/"; + if(day < 10) + the_time += "0"; + the_time += std::to_string(day) + " " + std::to_string(hours) + ":"; + if(minutes < 10) + the_time += "0"; + the_time += std::to_string(minutes) + " UTC"; + // Returns the date/time. + return the_time; +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::generate_simulated_adjacency_list() +{ + // Initializes the container. + simulated_adjacency_list.clear(); + simulated_adjacency_list.resize(nb_vertices); + // Generates the adjacency list. + double kappa1, theta1, dtheta, prob; + for(int v1(0); v1=1){prob = 1 / (1 + std::pow((nb_vertices * dtheta) / (2 * PI * mu * kappa1 * kappa[v2]), beta));} + if(beta< 1){prob = 1 / (1 + std::pow((nb_vertices * dtheta) / (2 * PI), beta) * 1.0 / (mu * kappa1 * kappa[v2]));} + if(uniform_01(engine) < prob) + { + simulated_adjacency_list[v1].insert(v2); + simulated_adjacency_list[v2].insert(v1); + } + } + } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::infer_initial_positions() +{ + if(!QUIET_MODE) { std::clog << "Finding initial positions/ordering..."; } + if(!QUIET_MODE) { std::clog.flush(); } + // Gets the original ordering of vertices. + std::vector ordering; + std::vector raw_theta; + find_initial_ordering(ordering, raw_theta); + + if(ordering.size() != nb_vertices) + std::cout << TAB << "WARNING: All degree-one vertices have not all been reinserted. Does the original edgelist have more than one connected component." << std::endl; + + // Initializes the container for the angular positions. + theta.clear(); + theta.resize(nb_vertices); + + // Spaces the vertices based on the expected angular distance between (non-)neighbors. + int v0, v1, n(0); + double factor, int1, int2, b, tmp; + double norm = 0; + double possible_dtheta1, possible_dtheta2; + double dx = PI / EXP_DIST_NB_INTEGRATION_STEPS; + int sign(1);//define to be able to do the same trick with sign of beta for beta<1 + double avg_gap = 2 * PI / nb_vertices; + std::vector::iterator it = ordering.begin(); + std::vector::iterator end = ordering.end(); + // Identifies the first vertex (takes care of the periodic condition of the circle). + v0 = *ordering.rbegin(); + for(; it!=end; ++it, ++n) + { + // Identifies the second vertex. + v1 = *it; + // Numerical integration of the expected angular distance between two consecutive vertices. + int1 = 0; + int2 = 0; + tmp = 0; + // Adjusts the sign of beta according to whether the vertices are connected or not. + b = beta; + if(adjacency_list[v0].find(v1) == adjacency_list[v0].end()) + { + b = -beta; // not connected + sign = -1; // not connected + } + // Lower bound of the integral (no contribution if not connected). + if(b > 0) + { + int2 += 0.5; + } + // Upper bound of the integral. + if(beta>=1){tmp = std::exp(-PI / avg_gap) / ( 1 + std::pow(nb_vertices / (2.0 * mu * random_ensemble_kappa_per_degree_class[degree[v0]] * random_ensemble_kappa_per_degree_class[degree[v1]] ), b) );} + if(beta< 1){tmp = std::exp(-PI / avg_gap) / ( 1 + std::pow(nb_vertices / 2.0, b) * std::pow(mu * random_ensemble_kappa_per_degree_class[degree[v0]] * random_ensemble_kappa_per_degree_class[degree[v1]], -sign ));} + int1 += PI * tmp / 2; + int2 += tmp / 2; + // In-between points. + for(double da = dx; da < PI; da += dx) + { + if(beta>=1){tmp = std::exp(-da / avg_gap) / ( 1 + std::pow(nb_vertices * da / (2.0 * PI * mu * random_ensemble_kappa_per_degree_class[degree[v0]] * random_ensemble_kappa_per_degree_class[degree[v1]] ), b) );} + if(beta< 1){tmp = std::exp(-da / avg_gap) / ( 1 + std::pow(nb_vertices * da / (2.0 * PI), b) * std::pow(mu * random_ensemble_kappa_per_degree_class[degree[v0]] * random_ensemble_kappa_per_degree_class[degree[v1]], -sign ));} + int1 += da * tmp; + int2 += tmp; + } + // Uses the value of the expected gap to position the vertices. + /*TEST*/possible_dtheta1 = int1 / int2; + /*TEST*/possible_dtheta2 = PI - std::fabs(PI - std::fabs(raw_theta[v1] - raw_theta[v0])); + if(possible_dtheta1 > possible_dtheta2) + { + norm += possible_dtheta1; + } + else + { + norm += possible_dtheta2; + } + // norm += int1 / int2; + theta[v1] = norm; + // Switches the identities of the vertices prior to the next gap. + + v0 = v1; + } + if(!QUIET_MODE) { std::clog << std::endl << TAB << "Sum of the angular positions (before adjustment): " << norm << std::endl; } + // Rescales the angles to limit their span in the [0, 2 PI) range. + norm /= 2 * PI; + for(int v(0); v NUMERICAL_CONVERGENCE_THRESHOLD_3) + { + keep_going = true; + continue; + } + } + // Modifies the value of the kappas prior to the next iteration, if required. + if(keep_going) + { + for(int v(0); v= KAPPA_MAX_NB_ITER_CONV) + { + if(!QUIET_MODE) { std::clog << TAB << "WARNING: maximum number of iterations reached before convergence. This limit can be" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << " adjusted by setting the parameters KAPPA_MAX_NB_ITER_CONV to desired value." << std::endl; } + } + else + { + if(!QUIET_MODE) { std::clog << TAB << "Convergence reached after " << cnt << " iterations." << std::endl; } + } + if(!QUIET_MODE) { std::clog << " ...............................done." << std::endl; } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::infer_kappas_given_beta_for_degree_class() +{ + // Variable. + double prob_conn; + // Parameters. + if(!NUMERIC_MU_MODE && beta > 1){mu = beta * std::sin(PI / beta) / (2.0 * PI * average_degree);} + else if(!NUMERIC_MU_MODE && beta < 1){mu = (1 - beta) * std::pow(2, -beta) * std::pow(nb_vertices, beta - 1) / average_degree;} + else if(!NUMERIC_MU_MODE && beta == 1){mu = 1.0 / (2 * average_degree * std::log(nb_vertices));} + else {calculate_numerical_mu();} + // Iterators. + std::set::iterator it1, it2, end; + // Initializes the kappas for each degree class. + it1 = degree_class.begin(); + end = degree_class.end(); + for(; it1!=end; ++it1) + { + random_ensemble_kappa_per_degree_class[*it1] = *it1; + } + + // Finds the values of kappa generating the degree classes, given the parameters. + int cnt = 0; + bool keep_going = true; + while( keep_going && (cnt < KAPPA_MAX_NB_ITER_CONV) ) + { + // Initializes the expected degree of each degree class. + it1 = degree_class.begin(); + end = degree_class.end(); + for(; it1!=end; ++it1) + { + random_ensemble_expected_degree_per_degree_class[*it1] = 0; + } + // Computes the expected degrees given the actual kappas. + it1 = degree_class.begin(); + end = degree_class.end(); + for(; it1!=end; ++it1) + { + it2 = it1; + prob_conn = hyp2f1a(beta, nb_vertices / 2.0, mu * random_ensemble_kappa_per_degree_class[*it1] * random_ensemble_kappa_per_degree_class[*it2]); + random_ensemble_expected_degree_per_degree_class[*it1] += prob_conn * (degree2vertices[*it2].size() - 1); + for(++it2; it2!=end; ++it2) + { + prob_conn = hyp2f1a(beta, nb_vertices / 2.0, mu * random_ensemble_kappa_per_degree_class[*it1] * random_ensemble_kappa_per_degree_class[*it2]); + random_ensemble_expected_degree_per_degree_class[*it1] += prob_conn * degree2vertices[*it2].size(); + random_ensemble_expected_degree_per_degree_class[*it2] += prob_conn * degree2vertices[*it1].size(); + } + } + // Verifies convergence. + keep_going = false; + it1 = degree_class.begin(); + end = degree_class.end(); + for(; it1!=end; ++it1) + { + if(std::fabs(random_ensemble_expected_degree_per_degree_class[*it1] - *it1) > NUMERICAL_CONVERGENCE_THRESHOLD_1) + { + keep_going = true; + } + } + // Modifies the value of the kappas prior to the next iteration, if required. + if(keep_going) + { + it1 = degree_class.begin(); + end = degree_class.end(); + for(; it1!=end; ++it1) + { + random_ensemble_kappa_per_degree_class[*it1] += (*it1 - random_ensemble_expected_degree_per_degree_class[*it1]) * uniform_01(engine); + random_ensemble_kappa_per_degree_class[*it1] = std::fabs(random_ensemble_kappa_per_degree_class[*it1]); + } + } + ++cnt; + } + if(cnt >= KAPPA_MAX_NB_ITER_CONV) + { + if(!QUIET_MODE) { std::clog << std::endl; } + // if(!QUIET_MODE) { std::clog << "WARNING: maximum number of iterations reached before convergence" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "WARNING: maximum number of iterations reached before convergence. This limit can be" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << " adjusted by setting the parameters KAPPA_MAX_NB_ITER_CONV to desired value." << std::endl; } + if(!QUIET_MODE) { std::clog << TAB; } + if(!QUIET_MODE) { std::clog << std::fixed << std::setw(11) << " " << " "; } + } +} + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::calculate_numerical_mu() +{ + // Initialize mu by its value for N -> Infinity. + if(beta> 1){mu = beta * std::sin(PI/beta) / (2*PI*average_degree);} + if(beta==1){mu = 1.0 / (2 * average_degree * std::log(nb_vertices));} + if(beta< 1){mu = (1-beta)/(std::pow(2,beta)*average_degree*std::pow(nb_vertices,1-beta));} + //Newtons method to find the value of mu where in the model the expected degree is the average degree. + //Variables. + std::set::iterator it1,it2; + bool keep_going(true); + int cnt(0); + int k1,k2, Nk1, Nk2; + double int1,int2; + + //Stop when convergence or max steps has been reached. + while(keep_going && cnt < FINDING_MU_STEPS){ + //Initialize integrals. int1 is the average degree and int2 is the derivative of the average degree wrt mu. + int1 = 0; + int2 = 0; + //The loops represent the hidden degree integrals, in this case sums. Here we assume kappa_i = k_i. + for ( it1 = degree_class.begin() ; it1 != degree_class.end() ; ++it1){ + k1 = *it1; + Nk1 = degree2vertices[*it1].size(); + for (it2 = degree_class.begin() ; it2 != degree_class.end() ; ++it2){ + k2 = *it2; + Nk2 = degree2vertices[*it2].size(); + //The integrand depends on beta. The hidden degree density is here approximated by Pk = Nk/N. + int1 += 1./nb_vertices *Nk1*Nk2* hyp2f1a(beta, nb_vertices*1./2,mu*k1*k2); + if (beta>1) { + int2 += 1./nb_vertices *Nk1*Nk2* 1./(1+std::pow(nb_vertices/(2*mu*k1*k2),beta)); + } + else if (beta>0){ + int2 += 1./nb_vertices *Nk1*Nk2* 1./(1+std::pow(nb_vertices*1./2,beta)/(mu*k1*k2)); + } + else{ + int2 += 1./nb_vertices *Nk1*Nk2* 1./(mu*k1*k2 + 2 + 1.0 /(mu*k1*k2)); + } + } + } + //Checks if the goal has been reached, if so, exit the loop. + if(std::abs(int1 - average_degree) < NUMERICAL_CONVERGENCE_THRESHOLD_2){ + keep_going = false; + } + else{ + //Newtons method calculates the next step. + if (beta>=1){mu -= mu * (int1 - average_degree)/ (int1 - int2);} + else if (beta>0){mu -= beta*mu * (int1 - average_degree)/ (int1 - int2);} + else{mu -= mu * (int1 - average_degree) / int2;} + //If overshoots to negative values enforce trying zero. + if (mu<=0){mu = NUMERICAL_ZERO;} + ++cnt; + } + } + + // If the loop above is exitted before convergence is reached, use the analytic approximation of mu instead. + if (cnt >= FINDING_MU_STEPS){ + if(!QUIET_MODE) { std::clog << TAB << "WARNING: maximum number of iterations reached before convergence. This limit can be" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << " adjusted by setting the parameters FINDING_MU_STEPS to desired value. Using" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << " analytic approximation of mu instead." << std::endl; } + if(beta> 1){mu = beta * std::sin(PI/beta) / (2*PI*average_degree);} + if(beta==1){mu = 1.0 / (2 * average_degree * std::log(nb_vertices));} + if(beta< 1){mu = (1-beta)/(std::pow(2,beta)*average_degree*std::pow(nb_vertices,1-beta));} + NUMERIC_MU_MODE = false; + } + + +} + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::infer_parameters() +{ + if(!QUIET_MODE) { std::clog << "Inferring parameters..."; } + if(!CUSTOM_BETA) + { + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << TAB; } + if(!QUIET_MODE) { std::clog << std::fixed << std::setw(11) << "beta" << " "; } + if(!QUIET_MODE) { std::clog << std::fixed << std::setw(20) << "avg. clustering" << " "; } + if(!QUIET_MODE) { std::clog << std::endl; } + // Sets initial value to beta. + beta = 2 + uniform_01(engine); + // Iterates until convergence is reached. + double beta_max = -1; + double beta_min; + bool CHECKED_METRICITY = false; + if(!ALL_BETA_MODE){beta_min = 1;} + else{beta_min = 0;} + random_ensemble_average_clustering = 10; // dummy value to enter the while loop. + while( true ) + { + if(!QUIET_MODE) { std::clog << TAB; } + if(!QUIET_MODE) { std::clog << std::fixed << std::setw(11) << beta << " "; } + if(!QUIET_MODE) { std::clog.flush(); } + // Infers the values of kappa. + infer_kappas_given_beta_for_degree_class(); + // Computes the cumulative distribution used in the MC integration. + build_cumul_dist_for_mc_integration(); + // Computes the ensemble clustering. + compute_random_ensemble_clustering(); + if(!QUIET_MODE) { std::clog << std::fixed << std::setw(20) << random_ensemble_average_clustering << " "; } + if(!QUIET_MODE) { std::clog << std::endl; } + + // Checks if the expected clustering is close enough. + if( std::fabs(random_ensemble_average_clustering - average_clustering) < NUMERICAL_CONVERGENCE_THRESHOLD_1 ) + { + break; + } + + // Modifies the bounds on beta if another iteration is required. + if(random_ensemble_average_clustering > average_clustering) + { + beta_max = beta; + beta = (beta_max + beta_min) / 2; + if(!ALL_BETA_MODE && beta < BETA_ABS_MIN_GEOMETRIC) + { + if(!QUIET_MODE) { std::clog << "WARNING: value too close to 1, using beta = " << std::fixed << std::setw(11) << beta << "."; } + if(!QUIET_MODE) { std::clog << std::endl; } + break; + } + if(ALL_BETA_MODE && beta < BETA_ABS_MIN_NON_GEOMETRIC) + { + if(!QUIET_MODE) { std::clog << "WARNING: value too close to 0, using beta = " << std::fixed << std::setw(11) << beta << "."; } + if(!QUIET_MODE) { std::clog << std::endl; } + break; + } + if(ALL_BETA_MODE && !CHECKED_METRICITY && beta < 1) + { + if(!QUIET_MODE) { std::clog << "beta < 1: checking metricity of the network ..." << std::endl; } + rewire_atzero(); + CHECKED_METRICITY = true; + } + } + else + { + beta_min = beta; + if(beta_max == -1) + { + beta *= 1.5; + } + else + { + beta = (beta_max + beta_min) / 2; + } + } + if(beta > BETA_ABS_MAX) + { + if(!QUIET_MODE) { std::clog << "WARNING: value too high, using beta = " << std::fixed << std::setw(11) << beta << "."; } + if(!QUIET_MODE) { std::clog << std::endl; } + break; + } + } + } + else + { + // Infers the values of kappa. + infer_kappas_given_beta_for_degree_class(); + // Computes the cumulative distribution used in the MC integration. + build_cumul_dist_for_mc_integration(); + // Computes the ensemble clustering. + compute_random_ensemble_clustering(); + } + // Computes the ensemble average degree. + compute_random_ensemble_average_degree(); + // Sets the kappas. + kappa.clear(); + kappa.resize(nb_vertices); + for(int v(0); vfirst << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Maximum degree: " << (--random_ensemble_expected_degree_per_degree_class.end())->first << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Average clustering: " << random_ensemble_average_clustering << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Parameters" << std::endl; } + if(!CUSTOM_BETA) + { + if(!QUIET_MODE) { std::clog << TAB << " - beta: " << beta << std::endl; } + } + else + { + if(!QUIET_MODE) { std::clog << TAB << " - beta: " << beta << " (custom)" << std::endl; } + } + if(!QUIET_MODE) { std::clog << TAB << " - mu: " << mu << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << " - radius_S1 (R): " << nb_vertices / (2 * PI) << std::endl; } + if(!QUIET_MODE) { std::clog << std::endl; } + + // Cleans containers that are no longer useful. + cumul_prob_kgkp.clear(); + degree2vertices.clear(); + random_ensemble_expected_degree_per_degree_class.clear(); + // random_ensemble_kappa_per_degree_class.clear(); +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::initialize() +{ + // Sets the default rootname for output files. + if(CUSTOM_OUTPUT_ROOTNAME_MODE == false) + { + size_t lastdot = EDGELIST_FILENAME.find_last_of("."); + if(lastdot == std::string::npos) + { + ROOTNAME_OUTPUT = EDGELIST_FILENAME; + } + ROOTNAME_OUTPUT = EDGELIST_FILENAME.substr(0, lastdot); + } + // // Gets current time. + // time0 = std::time(NULL); + // Initializes the random number generator. + if(!CUSTOM_SEED) + { + SEED = std::time(NULL); + } + engine.seed(SEED); + // Change the stream std::clog to a file. + if(!QUIET_MODE) + { + if(!VERBOSE_MODE) + { + logfile.open(ROOTNAME_OUTPUT + ".inf_log"); + // Get the rdbuf of clog. + // We need it to reset the value before exiting. + old_rdbuf = std::clog.rdbuf(); + // Set the rdbuf of clog. + std::clog.rdbuf(logfile.rdbuf()); + } + } + // Outputs options and parameters on screen. + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "===========================================================================================" << std::endl; } + if(!QUIET_MODE) { std::clog << "Mercator: accurate embeddings of graphs in the S1 space" << std::endl; } + if(!QUIET_MODE) { std::clog << "version: " << VERSION << std::endl; } + if(!QUIET_MODE) { std::clog << "started on: " << format_time(time_started) << std::endl; } + if(!QUIET_MODE) { std::clog << "edgelist filename: " << EDGELIST_FILENAME << std::endl; } + if(REFINE_MODE) + { + if(!QUIET_MODE) { std::clog << "inferred positions filename: " << ALREADY_INFERRED_PARAMETERS_FILENAME << std::endl; } + } + if(!QUIET_MODE) { std::clog << "seed: " << SEED << std::endl; } + + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "Loading edgelist..."; } + load_edgelist(); + if(!QUIET_MODE) { std::clog << "...................................................................done." << std::endl; } + + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "Checking number of connected components..."; } + check_connected_components(); + if(!QUIET_MODE) { std::clog << "............................................done." << std::endl; } + + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "Analyzing degrees..."; } + analyze_degrees(); + if(!QUIET_MODE) { std::clog << "..................................................................done." << std::endl; } + + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "Computing local clustering..."; } + compute_clustering(); + if(!QUIET_MODE) { std::clog << ".........................................................done." << std::endl; } + + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "Ordering vertices..."; } + order_vertices(); + if(!QUIET_MODE) { std::clog << "..................................................................done." << std::endl; } + if(!QUIET_MODE) { std::clog << std::endl; } + + // Sets the decimal precision of the log. + std::clog.precision(4); + + // Sets the width of the columns in the output files. + width_values = 15; + width_names = 14; + for(int v(0), l; v width_names) + { + width_names = l; + } + } + width_names += 1; + + if(!QUIET_MODE) { std::clog << "Properties of the graph" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Nb vertices: " << nb_vertices << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Nb edges: " << nb_edges << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Average degree: " << average_degree << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Minimum degree: " << *(degree_class.begin()) << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Maximum degree: " << *(--degree_class.end()) << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Nb of degree class: " << degree_class.size() << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "Average clustering: " << average_clustering << std::endl; } + if(!QUIET_MODE) { std::clog << std::endl; } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::load_already_inferred_parameters() +{ + // Stream object. + std::stringstream one_line; + // String objects. + std::string full_line, name1_str, name2_str, name3_str; + // Resets the containers. + kappa.clear(); + kappa.resize(nb_vertices); + theta.clear(); + theta.resize(nb_vertices); + // Opens the stream and terminates if the operation did not succeed. + std::fstream hidden_variables_file(ALREADY_INFERRED_PARAMETERS_FILENAME.c_str(), std::fstream::in); + if( !hidden_variables_file.is_open() ) + { + std::cerr << "Could not open file: " << ALREADY_INFERRED_PARAMETERS_FILENAME << "." << std::endl; + std::terminate(); + } + // Extracts the beta and mu parameters. + // Ignores the first 9 lines of the file. + for(int l(0); l<8; ++l) + { + std::getline(hidden_variables_file, full_line); + } + // Gets the 10th lines containing the value of beta. + std::getline(hidden_variables_file, full_line); + hidden_variables_file >> std::ws; + one_line.str(full_line); + one_line >> std::ws; + one_line >> name1_str >> std::ws; + one_line >> name1_str >> std::ws; + one_line >> name1_str >> std::ws; + one_line >> name1_str >> std::ws; + beta = std::stod(name1_str); + one_line.clear(); + // Gets the 11th lines containing the value of mu. + std::getline(hidden_variables_file, full_line); + hidden_variables_file >> std::ws; + one_line.str(full_line); + one_line >> std::ws; + one_line >> name1_str >> std::ws; + one_line >> name1_str >> std::ws; + one_line >> name1_str >> std::ws; + one_line >> name1_str >> std::ws; + mu = std::stod(name1_str); + one_line.clear(); + // Reads the hidden variables file line by line. + while( !hidden_variables_file.eof() ) + { + // Reads a line of the file. + std::getline(hidden_variables_file, full_line); + hidden_variables_file >> std::ws; + one_line.str(full_line); + one_line >> std::ws; + one_line >> name1_str >> std::ws; + // Skips lines of comment. + if(name1_str == "#") + { + one_line.clear(); + continue; + } + one_line >> name2_str >> std::ws; + kappa[ Name2Num[name1_str] ] = std::stod(name2_str); + one_line >> name3_str >> std::ws; + theta[ Name2Num[name1_str] ] = std::stod(name3_str); + one_line.clear(); + } + // Closes the stream. + hidden_variables_file.close(); + // Clears unused objects. + Name2Num.clear(); +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::load_edgelist() +{ + // Stream objects. + std::ifstream edgelist_file; + std::stringstream one_line; + // Variables. + int v1, v2; + // String objects. + std::string full_line, name1_str, name2_str; + // Iterator objects. + std::map< std::string, int >::iterator name_it; + // Resets the number of vertices and of edges. + nb_vertices = 0; + nb_edges = 0; + // Resets the containers. + adjacency_list.clear(); + // Opens the stream and terminates if the operation did not succeed. + edgelist_file.open(EDGELIST_FILENAME.c_str(), std::ios_base::in); + if( !edgelist_file.is_open() ) + { + std::cerr << "Could not open file: " << EDGELIST_FILENAME << "." << std::endl; + std::terminate(); + } + else + { + // Reads the edgelist file line by line. + while( !edgelist_file.eof() ) + { + // Reads a line of the file. + std::getline(edgelist_file, full_line); edgelist_file >> std::ws; + one_line.str(full_line); one_line >> std::ws; + one_line >> name1_str >> std::ws; + // Skips lines of comment. + if(name1_str == "#") + { + one_line.clear(); + continue; + } + one_line >> name2_str >> std::ws; + one_line.clear(); + // Does not consider self-loops. + if(name1_str != name2_str) + { + // Is name1 new? + name_it = Name2Num.find(name1_str); + if( name_it == Name2Num.end() ) + { + // New vertex. + v1 = nb_vertices; + Name2Num[name1_str] = v1; + Num2Name.push_back(name1_str); + adjacency_list.push_back(std::set()); + ++nb_vertices; + } + else + { + // Known vertex. + v1 = name_it->second; + } + // Is name2 new? + name_it = Name2Num.find(name2_str); + if( name_it == Name2Num.end() ) + { + // New vertex. + v2 = nb_vertices; + Name2Num[name2_str] = v2; + Num2Name.push_back(name2_str); + adjacency_list.push_back(std::set()); + ++nb_vertices; + } + else + { + // Known vertex. + v2 = name_it->second; + } + // Adds the edge to the adjacency list (multiedges are ignored due to std::set). + std::pair< std::set::iterator, bool > add1 = adjacency_list[v1].insert(v2); + std::pair< std::set::iterator, bool > add2 = adjacency_list[v2].insert(v1); + if(add1.second && add2.second) // Both bool should always agree. + { + ++nb_edges; + } + } + } + } + // Closes the stream. + edgelist_file.close(); + if(!REFINE_MODE) + { + // Clears unused objects. + Name2Num.clear(); + } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::order_vertices() +{ + // Containers related to the results of the onion decomposition. + std::vector coreness(nb_vertices); + std::vector od_layer(nb_vertices); + // Extracts the onion decomposition. + extract_onion_decomposition(coreness, od_layer); + // Orders the vertices based on their layer. + std::set< std::pair > > layer_set; + for(int v(0); v > >::reverse_iterator it = layer_set.rbegin(); + std::set< std::pair > >::reverse_iterator end = layer_set.rend(); + for(int v(0); it!=end; ++it, ++v) + { + ordered_list_of_vertices[v] = it->second.second; + } + layer_set.clear(); +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +int embeddingS1_t::refine_angle(int v1) +{ + // Variables. + int has_moved = 0; + double tmp_angle; + double tmp_loglikelihood; + double best_angle = theta[v1]; + // Iterators. + std::set::iterator it2, end; + // Computes the current loglikelihood. + double previous_loglikelihood = 0; + for(int v2(0); v2 (2 * PI)) + average_theta = average_theta - (2 * PI); + while(average_theta < 0) + average_theta = average_theta + (2 * PI); + + // Finds the largest angular distance between the neighbor and the average position. + double max_angle = MIN_TWO_SIGMAS_NORMAL_DIST; + it2 = adjacency_list[v1].begin(); + for(; it2!=end; ++it2) + { + da = PI - std::fabs(PI - std::fabs(average_theta - theta[*it2])); + if(da > max_angle) + { + max_angle = da; + } + } + max_angle /= 2; + + // Considers various wisely chosen new angular positions and keeps the best. + int _nb_new_angles_to_try = MIN_NB_ANGLES_TO_TRY * std::max(1.0, std::log(nb_vertices)); + for(int e(0); e<_nb_new_angles_to_try; ++e) + { + // Gets the angle in the standard range. + tmp_angle = (normal_01(engine) * max_angle) + average_theta; + while(tmp_angle > (2 * PI)) + tmp_angle = tmp_angle - (2 * PI); + while(tmp_angle < 0) + tmp_angle = tmp_angle + (2 * PI); + + // Computes the local loglikelihood. + tmp_loglikelihood = 0; + for(int v2(0); v2 best_loglikelihood) + { + best_loglikelihood = tmp_loglikelihood; + best_angle = tmp_angle; + has_moved = 1; + } + } + + // Registers the best position found. + theta[v1] = best_angle; + // Returns 1 if the vertex changed position, and 0 otherwise. + return has_moved; +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::refine_positions() +{ + if(!QUIET_MODE) { std::clog << "Refining the positions..."; } + if(!QUIET_MODE) { std::clog << std::endl; } + + // // Imposes a global random shift on the angular positions. + // double theta_shift = 2 * PI * uniform_01(engine); + // // double theta_shift = PI / 2; + // for(int i(0); i= (2 * PI)) + // theta[i] = theta[i] - (2 * PI); + // while(theta[i] < 0) + // theta[i] = theta[i] + (2 * PI); + // } + + double start_time, stop_time; + std::string vertices_range; + int delta_nb_vertices = nb_vertices / 19.999999; + if(delta_nb_vertices < 1) { delta_nb_vertices = 1; } + int width = 2 * (std::log10(nb_vertices) + 1) + 6; + for(int v_i(0), v_f(0), v_m, n_v; v_f nb_vertices) ? nb_vertices : v_f; + n_v = v_f - v_i; + start_time = time_since_epoch_in_seconds(); + if(!QUIET_MODE) { vertices_range = "[" + std::to_string(v_i+1) + "," + std::to_string(v_f) + "]..."; } + if(!QUIET_MODE) { std::clog << TAB << "...of vertices " << std::setw(width) << vertices_range; } + for(v_m = 0; v_iNB_VERTICES_IN_CORE) ? std::to_string(NB_VERTICES_IN_CORE) : std::to_string(nb_vertices) ) + " vertices, iteration #" + std::to_string(j+1) + ")"; std::clog.clear(); } + // for(int i(0); (i NB_VERTICES_IN_CORE) + // { + // if(!QUIET_MODE) { std::clog << TAB << "refining the positions of the remaining vertices (" + std::to_string(nb_vertices - NB_VERTICES_IN_CORE) + " vertices)"; std::clog.clear(); } + // for(int i(NB_VERTICES_IN_CORE); i bins; + std::map::iterator it; + int bound = 20; + int cnt = 0; + double dt = 0.05; + for(double t(-bound), tt(bound + 0.000001); t n(bins.size(), 0); + std::vector p(bins.size(), 0); + std::vector x(bins.size(), 0); + // Computes the connection probability for every pair of vertices. + double k1; + double t1; + double da; + double dist; + + for(int v1(0), i; v1=1){dist = (nb_vertices * da) / (2 * PI * mu * k1 * kappa[v2]);} + if(beta< 1){dist = std::pow((nb_vertices * da) / (2 * PI), beta) / (mu * k1 * kappa[v2]);} + i = bins.lower_bound(dist)->second; + n[i] += 1; + x[i] += dist; + if(adjacency_list[v1].find(v2) != adjacency_list[v1].end()) + { + p[i] += 1; + } + } + } + // Writes the connection probability into a file. + std::string pconn_filename = ROOTNAME_OUTPUT + ".inf_pconn"; + std::fstream pconn_file(pconn_filename.c_str(), std::fstream::out); + if( !pconn_file.is_open() ) + { + std::cerr << "Could not open file: " << pconn_filename << "." << std::endl; + std::terminate(); + } + pconn_file << "#"; + pconn_file << std::setw(width_values - 1) << "RescaledDist" << " "; + pconn_file << std::setw(width_values) << "InfConnProb" << " "; + pconn_file << std::setw(width_values) << "ThConnProb" << " "; + pconn_file << std::endl; + for(int i(0), ii(n.size()); i 0) + { + pconn_file << std::setw(width_values) << x[i] / n[i] << " "; + pconn_file << std::setw(width_values) << p[i] / n[i] << " "; + if(beta>=1){pconn_file << std::setw(width_values) << 1 / (1 + std::pow(x[i] / n[i], beta) ) << " ";} + if(beta< 1){pconn_file << std::setw(width_values) << 1 / (1 + x[i] / n[i]) << " ";} + pconn_file << std::endl; + } + } + // Closes the stream. + pconn_file.close(); + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "=> Inferred connection probability saved to " << ROOTNAME_OUTPUT + ".inf_pconn" << std::endl; } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::save_inferred_coordinates() +{ + // Finds the minimal and maximal values of kappa. + double kappa_min = *std::min_element(kappa.begin(), kappa.end()); + double kappa_max = *std::max_element(kappa.begin(), kappa.end()); + // Computes the hyperbolic radius (adjusts it in case some vertices have a negative radial position). + double hyp_radius; + if(beta>=1){hyp_radius = 2 * std::log( nb_vertices / (PI * mu * kappa_min * kappa_min) );} + if(beta< 1){hyp_radius = 2 * beta * std::log( nb_vertices / PI) - 2 * std::log(mu * kappa_min * kappa_min);} + double min_radial_position = hyp_radius - 2 * std::log( kappa_min / kappa_max ); + bool warning = false; + if(min_radial_position < 0) + { + hyp_radius += std::fabs(min_radial_position); + warning = true; + } + // Sets the name of the file to write the hidden variables into. + std::string coordinates_filename = ROOTNAME_OUTPUT + ".inf_coord"; + // // Gets the current time. + // time1 = std::time(NULL); + // Opens the stream and terminates if the operation did not succeed. + std::fstream coordinates_file(coordinates_filename.c_str(), std::fstream::out); + if( !coordinates_file.is_open() ) + { + std::cerr << "Could not open file: " << coordinates_filename << "." << std::endl; + std::terminate(); + } + // Writes the header. + coordinates_file << "# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=" << std::endl; + coordinates_file << "# Embedding started at: " << format_time(time_started) << std::endl; + coordinates_file << "# Ended at: " << format_time(time_ended) << std::endl; + coordinates_file << "# Elapsed CPU time: " << time5 - time0 << " seconds" << std::endl; + coordinates_file << "# Edgelist file: " << EDGELIST_FILENAME << std::endl; + coordinates_file << "#" << std::endl; + coordinates_file << "# Parameters" << std::endl; + coordinates_file << "# - nb. vertices: " << nb_vertices << std::endl; + coordinates_file << "# - beta: " << beta << std::endl; + coordinates_file << "# - mu: " << mu << std::endl; + coordinates_file << "# - radius_S1: " << nb_vertices / (2 * PI) << std::endl; + coordinates_file << "# - radius_H2: " << hyp_radius << std::endl; + coordinates_file << "# - kappa_min: " << kappa_min << std::endl; + coordinates_file << "# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=" << std::endl; + coordinates_file << "#"; + coordinates_file << std::setw(width_names - 1) << "Vertex" << " "; + coordinates_file << std::setw(width_values) << "Inf.Kappa" << " "; + coordinates_file << std::setw(width_values) << "Inf.Theta" << " "; + coordinates_file << std::setw(width_values) << "Inf.Hyp.Rad." << " "; + coordinates_file << std::endl; + // Structure containing the desired method to compared strings (put shorter ones before longer ones). + struct compare + { + bool operator()(const std::pair& lhs, const std::pair& rhs) const + { + if(lhs.first.size() == rhs.first.size()) + { + if(lhs.first == rhs.first) + { + return lhs.second < rhs.second; + } + else + { + return lhs.first < rhs.first; + } + } + else + { + return lhs.first.size() < rhs.first.size(); + } + } + }; + // Writes the hidden variables. + std::set< std::pair, compare > ordered_names; + for(int v(0); v >::iterator it = ordered_names.begin(); + std::set< std::pair >::iterator end = ordered_names.end(); + for(int v; it!=end; ++it) + { + v = it->second; + coordinates_file << std::setw(width_names) << it->first << " "; + coordinates_file << std::setw(width_values) << kappa[v] << " "; + coordinates_file << std::setw(width_values) << theta[v] << " "; + coordinates_file << std::setw(width_values) << hyp_radius - 2 * std::log( kappa[v] / kappa_min ) << " "; + // coordinates_file << std::setw(width) << 2 * std::log( nb_vertices / (PI * mu * kappa_min * kappa[v]) ) << " "; + coordinates_file << std::endl; + } + coordinates_file << "#" << std::endl; + coordinates_file << "# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=" << std::endl; + coordinates_file << "# Internal parameters and options" << std::endl; + coordinates_file << "# " << TAB << "ALREADY_INFERRED_PARAMETERS_FILENAME " << ALREADY_INFERRED_PARAMETERS_FILENAME << std::endl; + coordinates_file << "# " << TAB << "BETA_ABS_MAX " << BETA_ABS_MAX << std::endl; + coordinates_file << "# " << TAB << "BETA_ABS_MIN_GEOMETRIC " << BETA_ABS_MIN_GEOMETRIC << std::endl; + coordinates_file << "# " << TAB << "BETA_ABS_MIN_NON_GEOMETRIC " << BETA_ABS_MIN_NON_GEOMETRIC << std::endl; + coordinates_file << "# " << TAB << "CHARACTERIZATION_MODE " << (CHARACTERIZATION_MODE ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "CHARACTERIZATION_NB_GRAPHS " << CHARACTERIZATION_NB_GRAPHS << std::endl; + coordinates_file << "# " << TAB << "CLEAN_RAW_OUTPUT_MODE " << (CLEAN_RAW_OUTPUT_MODE ? "true" : "false") << std::endl; + // coordinates_file << "# " << TAB << "CLOSE_ANGULAR_RANGE_FACTOR " << CLOSE_ANGULAR_RANGE_FACTOR << std::endl; + coordinates_file << "# " << TAB << "CUSTOM_BETA " << (CUSTOM_BETA ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "CUSTOM_INFERRED_COORDINATES " << (CUSTOM_INFERRED_COORDINATES ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "CUSTOM_OUTPUT_ROOTNAME_MODE " << (CUSTOM_OUTPUT_ROOTNAME_MODE ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "CUSTOM_SEED " << (CUSTOM_SEED ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "EDGELIST_FILENAME: " << EDGELIST_FILENAME << std::endl; + coordinates_file << "# " << TAB << "EXP_CLUST_NB_INTEGRATION_MC_STEPS " << EXP_CLUST_NB_INTEGRATION_MC_STEPS << std::endl; + coordinates_file << "# " << TAB << "EXP_DIST_NB_INTEGRATION_STEPS " << EXP_DIST_NB_INTEGRATION_STEPS << std::endl; + coordinates_file << "# " << TAB << "FINDING_MU_STEPS " << FINDING_MU_STEPS << std::endl; + coordinates_file << "# " << TAB << "KAPPA_MAX_NB_ITER_CONV " << KAPPA_MAX_NB_ITER_CONV << std::endl; + coordinates_file << "# " << TAB << "KAPPA_POST_INFERENCE_MODE " << (KAPPA_POST_INFERENCE_MODE ? "true" : "false") << std::endl; + // coordinates_file << "# " << TAB << "LIMIT_FOR_CONVERGENCE_CRITERION " << LIMIT_FOR_CONVERGENCE_CRITERION << std::endl; + // coordinates_file << "# " << TAB << "MAX_NB_ITER_MAXIMIZATION " << MAX_NB_ITER_MAXIMIZATION << std::endl; + coordinates_file << "# " << TAB << "MAXIMIZATION_MODE " << (MAXIMIZATION_MODE ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "METRICITY_TEST_NB_GRAPHS " << METRICITY_TEST_NB_GRAPHS << std::endl; + // coordinates_file << "# " << TAB << "MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD " << MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD << std::endl; + // coordinates_file << "# " << TAB << "MINIMAL_ANGULAR_RESOLUTION " << MINIMAL_ANGULAR_RESOLUTION << std::endl; + // coordinates_file << "# " << TAB << "NB_VERTICES_IN_CORE " << NB_VERTICES_IN_CORE << std::endl; + coordinates_file << "# " << TAB << "MIN_NB_ANGLES_TO_TRY " << MIN_NB_ANGLES_TO_TRY << std::endl; + coordinates_file << "# " << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_1 " << NUMERICAL_CONVERGENCE_THRESHOLD_1 << std::endl; + coordinates_file << "# " << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_2 " << NUMERICAL_CONVERGENCE_THRESHOLD_2 << std::endl; + coordinates_file << "# " << TAB << "NUMERICAL_CONVERGENCE_THRESHOLD_3 " << NUMERICAL_CONVERGENCE_THRESHOLD_3 << std::endl; + coordinates_file << "# " << TAB << "NUMERICAL_ZERO " << NUMERICAL_ZERO << std::endl; + coordinates_file << "# " << TAB << "QUIET_MODE " << (QUIET_MODE ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "REFINE_MODE " << (REFINE_MODE ? "true" : "false") << std::endl; + // coordinates_file << "# " << TAB << "REFINED_MAX_STEP_LENGTH_DIVISOR " << REFINED_MAX_STEP_LENGTH_DIVISOR << std::endl; + coordinates_file << "# " << TAB << "ROOTNAME_OUTPUT: " << ROOTNAME_OUTPUT << std::endl; + coordinates_file << "# " << TAB << "SEED " << SEED << std::endl; + coordinates_file << "# " << TAB << "VALIDATION_MODE " << (VALIDATION_MODE ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "ALL_BETA_MODE " << (ALL_BETA_MODE ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "NUMERIC_MU_MODE " << (NUMERIC_MU_MODE ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "VERBOSE_MODE " << (VERBOSE_MODE ? "true" : "false") << std::endl; + coordinates_file << "# " << TAB << "VERSION " << VERSION << std::endl; + coordinates_file << "# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=" << std::endl; + // Closes the stream. + coordinates_file.close(); + + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "=> Inferred coordinates saved to " << ROOTNAME_OUTPUT + ".inf_coord" << std::endl; } + + if(CLEAN_RAW_OUTPUT_MODE) + { + // Sets the name of the file to write the hidden variables into. + coordinates_filename = ROOTNAME_OUTPUT + ".inf_coord_raw"; + // Opens the stream and terminates if the operation did not succeed. + coordinates_file.open(coordinates_filename.c_str(), std::fstream::out); + if( !coordinates_file.is_open() ) + { + std::cerr << "Could not open file: " << coordinates_filename << "." << std::endl; + std::terminate(); + } + // Writes the hidden variables. + it = ordered_names.begin(); + end = ordered_names.end(); + for(int v; it!=end; ++it) + { + v = it->second; + // coordinates_file << it->first << " "; + coordinates_file << kappa[v] << " "; + coordinates_file << theta[v] << " "; + coordinates_file << hyp_radius - 2 * std::log( kappa[v] / kappa_min ) << " "; + coordinates_file << std::endl; + } + // Closes the stream. + coordinates_file.close(); + + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "=> Raw inferred coordinates also saved to " << ROOTNAME_OUTPUT + ".inf_coord_raw" << std::endl; } + } + + if(warning) + { + if(!QUIET_MODE) { std::clog << "WARNING: Hyperbolic radius has been adjusted to account for negative radial positions." << std::endl; } + } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::save_inferred_ensemble_characterization() +{ + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "Characterizing the inferred ensemble..." << std::endl; } + // Iterators. + std::map::iterator it2, end2; + std::map >::iterator it3, end3; + // Initializes the containers. + characterizing_inferred_ensemble_vprops.clear(); + characterizing_inferred_ensemble_vprops.resize(5); + for(int i(0); i(2, 0)); + } + characterizing_inferred_ensemble_vstat.clear(); + // Objects to compute the complementary cumulative degree distribution. + std::vector single_comp_cumul_degree_dist; + std::vector avg_comp_cumul_degree_dist; + std::vector std_comp_cumul_degree_dist; + std::vector nb_comp_cumul_degree_dist; + // Sets the number of graphs to be generated in function of the size of the original graph. + if(!CUSTOM_CHARACTERIZATION_NB_GRAPHS) + { + if (nb_vertices < 500 ) { CHARACTERIZATION_NB_GRAPHS = 1000; } + else if(nb_vertices < 1000 ) { CHARACTERIZATION_NB_GRAPHS = 500; } + else if(nb_vertices < 10000) { CHARACTERIZATION_NB_GRAPHS = 100; } + else { CHARACTERIZATION_NB_GRAPHS = 10; } + } + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "A total of " << CHARACTERIZATION_NB_GRAPHS << " graphs will be generated (chosen in function of the total number" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "of vertices). To change this value, set the flag 'CUSTOM_CHARACTERIZATION_NB_GRAPHS'" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "to 'true' and set the variable 'CHARACTERIZATION_NB_GRAPHS' to the desired value." << std::endl; } + if(!QUIET_MODE) { std::clog << std::endl; } + // Performs the simulations. + int delta_nb_graphs = CHARACTERIZATION_NB_GRAPHS / 19.999999; + if(delta_nb_graphs < 1) { delta_nb_graphs = 1; } + int width = 2 * (std::log10(CHARACTERIZATION_NB_GRAPHS) + 1) + 6; + double d1, value; + std::string graph_range; + double start_time, stop_time; + for(int g_i(0), g_f, d_max; g_i CHARACTERIZATION_NB_GRAPHS) ? CHARACTERIZATION_NB_GRAPHS : g_f; + start_time = time_since_epoch_in_seconds(); + if(!QUIET_MODE) { graph_range = "[" + std::to_string(g_i+1) + "," + std::to_string(g_f) + "]..."; } + if(!QUIET_MODE) { std::clog << TAB << "Generating and analyzing graphs " << std::setw(width) << graph_range; } + for(; g_i 0) + { + // Sum of the degree of neighbors. + value = simulated_sum_degree_of_neighbors[v1]; + characterizing_inferred_ensemble_vprops[1][v1][0] += value; + characterizing_inferred_ensemble_vprops[1][v1][1] += value * value; + // Average degree of neighbors. + value /= d1; + characterizing_inferred_ensemble_vprops[2][v1][0] += value; + characterizing_inferred_ensemble_vprops[2][v1][1] += value * value; + } + if(d1 > 1) + { + // Number of triangles attached on the vertex. + value = simulated_nb_triangles[v1]; + characterizing_inferred_ensemble_vprops[3][v1][0] += value; + characterizing_inferred_ensemble_vprops[3][v1][1] += value * value; + // Clustering coefficient. + value /= d1 * (d1 - 1) / 2; + characterizing_inferred_ensemble_vprops[4][v1][0] += value; + characterizing_inferred_ensemble_vprops[4][v1][1] += value * value; + } + } + // Compiles the various statistics about the degree classes. + it2 = simulated_stat_degree.begin(); + end2 = simulated_stat_degree.end(); + d_max = -1; + // single_comp_cumul_degree_dist.clear(); + for(int d, norm; it2!=end2; ++it2) + { + // Gets the degree class. + d = it2->first; + // Initializes the degree class if it has not been encountered yet. + if( characterizing_inferred_ensemble_vstat.find(d) == characterizing_inferred_ensemble_vstat.end() ) + { + characterizing_inferred_ensemble_vstat[d] = std::vector((2 * 5) + 1, 0); + } + // // Adjusts the size of the vector containing the complementary cumulative distribution if necessary. + if(d > d_max) + { + d_max = d; + single_comp_cumul_degree_dist.resize(d_max + 1, 0); + } + // Gets the number of vertices in this degree class. + norm = it2->second; + // Degree distribution. + value = simulated_stat_degree[d] / nb_vertices; + characterizing_inferred_ensemble_vstat[d][0] += value; + characterizing_inferred_ensemble_vstat[d][1] += value * value; + // Complementary cumulative degree distribution. + for(int q(0); q<=d; ++q) + { + single_comp_cumul_degree_dist[q] += value; + // comp_cumul_degree_dist_n[q] += 1; + } + // Sum of the degree of neighbors. + value = simulated_stat_sum_degree_neighbors[d] / norm; + characterizing_inferred_ensemble_vstat[d][2] += value; + characterizing_inferred_ensemble_vstat[d][3] += value * value; + // Average of the degree of neighbors. + value = simulated_stat_avg_degree_neighbors[d] / norm; + characterizing_inferred_ensemble_vstat[d][4] += value; + characterizing_inferred_ensemble_vstat[d][5] += value * value; + // Number of triangles attached on the vertex. + value = simulated_stat_nb_triangles[d] / norm; + characterizing_inferred_ensemble_vstat[d][6] += value; + characterizing_inferred_ensemble_vstat[d][7] += value * value; + // Clustering coefficient. + value = simulated_stat_clustering[d] / norm; + characterizing_inferred_ensemble_vstat[d][8] += value; + characterizing_inferred_ensemble_vstat[d][9] += value * value; + // Counts the number of time the degree class has been observed. + characterizing_inferred_ensemble_vstat[d][10] += 1; + } + // Counts which degree classes have been reached. + if((d_max + 1) > nb_comp_cumul_degree_dist.size()) + { + avg_comp_cumul_degree_dist.resize(d_max + 1, 0); + std_comp_cumul_degree_dist.resize(d_max + 1, 0); + nb_comp_cumul_degree_dist.resize(d_max + 1, 0); + } + for(int r(0); r<=d_max; ++r) + { + avg_comp_cumul_degree_dist[r] += single_comp_cumul_degree_dist[r]; + std_comp_cumul_degree_dist[r] += single_comp_cumul_degree_dist[r] * single_comp_cumul_degree_dist[r]; + nb_comp_cumul_degree_dist[r] += 1; + } + } + // Compiles the complementary cumulative degree distribution. + stop_time = time_since_epoch_in_seconds(); + if(!QUIET_MODE) { std::clog << "...done in " << std::setw(6) << std::fixed << stop_time - start_time << " seconds" << std::endl; } + } + // Finalizes the characterization. + for(int i(0); ifirst; + norm = characterizing_inferred_ensemble_vstat[d][10]; + for(int i(0); i<10; ++++i) + { + characterizing_inferred_ensemble_vstat[d][i + 0] /= norm; + if(norm > 1) + { + characterizing_inferred_ensemble_vstat[d][i + 1] /= norm; + characterizing_inferred_ensemble_vstat[d][i + 1] -= characterizing_inferred_ensemble_vstat[d][i + 0] * characterizing_inferred_ensemble_vstat[d][i + 0]; + characterizing_inferred_ensemble_vstat[d][i + 1] *= norm / (norm - 1); + if( characterizing_inferred_ensemble_vstat[d][i + 1] < 0 ) + { + characterizing_inferred_ensemble_vstat[d][i + 1] = 0; + } + else + { + characterizing_inferred_ensemble_vstat[d][i + 1] = std::sqrt( characterizing_inferred_ensemble_vstat[d][i + 1] ); + } + } + else + { + characterizing_inferred_ensemble_vstat[d][i + 1] = 0; + } + } + } + // Complete the characterization of the complementary cumulative degree distribution. + for(int i(0), ii(nb_comp_cumul_degree_dist.size()); i 0) + { + avg_comp_cumul_degree_dist[i] /= nb_comp_cumul_degree_dist[i]; + if(nb_comp_cumul_degree_dist[i] > 1) + { + std_comp_cumul_degree_dist[i] /= nb_comp_cumul_degree_dist[i]; + std_comp_cumul_degree_dist[i] -= avg_comp_cumul_degree_dist[i] * avg_comp_cumul_degree_dist[i]; + std_comp_cumul_degree_dist[i] *= nb_comp_cumul_degree_dist[i] / (nb_comp_cumul_degree_dist[i] - 1); + if(std_comp_cumul_degree_dist[i] < 0) + { + std_comp_cumul_degree_dist[i] = 0; + } + else + { + std_comp_cumul_degree_dist[i] = std::sqrt(std_comp_cumul_degree_dist[i]); + } + } + else + { + std_comp_cumul_degree_dist[i] = 0; + } + } + } + if(!QUIET_MODE) { std::clog << " ...............................................done." << std::endl; } + // Sets the name of the file to write the vertices properties into. + std::string vertex_properties_filename = ROOTNAME_OUTPUT + ".inf_vprop"; + // Opens the stream and terminates if the operation did not succeed. + std::fstream vertex_properties_file(vertex_properties_filename.c_str(), std::fstream::out); + if( !vertex_properties_file.is_open() ) + { + std::cerr << "Could not open file: " << vertex_properties_filename << "." << std::endl; + std::terminate(); + } + // Writes the header. + vertex_properties_file << "#"; + vertex_properties_file << std::setw(width_names - 1) << "Vertex" << " "; + vertex_properties_file << std::setw(width_values) << "Degree" << " "; + vertex_properties_file << std::setw(width_values) << "Avg.Degree" << " "; + vertex_properties_file << std::setw(width_values) << "Std.Degree" << " "; + vertex_properties_file << std::setw(width_values) << "Sum.Deg.N" << " "; + vertex_properties_file << std::setw(width_values) << "Avg.Sum.Deg.N" << " "; + vertex_properties_file << std::setw(width_values) << "Std.Sum.Deg.N" << " "; + vertex_properties_file << std::setw(width_values) << "Avg.Deg.N" << " "; + vertex_properties_file << std::setw(width_values) << "Avg.Avg.Deg.N" << " "; + vertex_properties_file << std::setw(width_values) << "Std.Avg.Deg.N" << " "; + vertex_properties_file << std::setw(width_values) << "NbTriang" << " "; + vertex_properties_file << std::setw(width_values) << "Avg.NbTriang" << " "; + vertex_properties_file << std::setw(width_values) << "Std.NbTriang" << " "; + vertex_properties_file << std::setw(width_values) << "Clustering" << " "; + vertex_properties_file << std::setw(width_values) << "Avg.Clustering" << " "; + vertex_properties_file << std::setw(width_values) << "Std.Clustering" << " "; + vertex_properties_file << std::endl; + // Structure containing the desired method to compared strings (put shorter ones before longer ones). + struct compare + { + bool operator()(const std::pair& lhs, const std::pair& rhs) const + { + if(lhs.first.size() == rhs.first.size()) + { + if(lhs.first == rhs.first) + { + return lhs.second < rhs.second; + } + else + { + return lhs.first < rhs.first; + } + } + else + { + return lhs.first.size() < rhs.first.size(); + } + } + }; + // Writes the hidden variables. + std::set< std::pair, compare > ordered_names; + for(int v(0); v >::iterator it = ordered_names.begin(); + std::set< std::pair >::iterator end = ordered_names.end(); + for(int v, d; it!=end; ++it) + { + v = it->second; + d = degree[v]; + vertex_properties_file << std::setw(width_names) << it->first << " "; + vertex_properties_file << std::setw(width_values) << degree[v] << " "; + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[0][v][0] << " "; + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[0][v][1] << " "; + vertex_properties_file << std::setw(width_values) << sum_degree_of_neighbors[v] << " "; + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[1][v][0] << " "; + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[1][v][1] << " "; + if(d > 0) + { + vertex_properties_file << std::setw(width_values) << sum_degree_of_neighbors[v] / d << " "; + } + else + { + vertex_properties_file << std::setw(width_values) << sum_degree_of_neighbors[v] << " "; + } + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[2][v][0] << " "; + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[2][v][1] << " "; + vertex_properties_file << std::setw(width_values) << nbtriangles[v] << " "; + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[3][v][0] << " "; + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[3][v][1] << " "; + if(d > 1) + { + vertex_properties_file << std::setw(width_values) << nbtriangles[v] / (d * (d-1) / 2) << " "; + } + else + { + vertex_properties_file << std::setw(width_values) << nbtriangles[v] << " "; + } + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[4][v][0] << " "; + vertex_properties_file << std::setw(width_values) << characterizing_inferred_ensemble_vprops[4][v][1] << " "; + vertex_properties_file << std::endl; + } + vertex_properties_file.close(); + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "=> Vertices properties of the inferred ensemble saved to " << ROOTNAME_OUTPUT + ".inf_vprop" << std::endl; } + + // Sets the name of the file to write the vertices properties into. + std::string vertex_stat_filename = ROOTNAME_OUTPUT + ".inf_vstat"; + // Opens the stream and terminates if the operation did not succeed. + std::fstream vertex_stat_file(vertex_stat_filename.c_str(), std::fstream::out); + if( !vertex_stat_file.is_open() ) + { + std::cerr << "Could not open file: " << vertex_stat_filename << "." << std::endl; + std::terminate(); + } + // Writes the header. + vertex_stat_file << "#"; + vertex_stat_file << std::setw(width_values - 1) << "Degree" << " "; + // vertex_stat_file << std::setw(width_values) << "DegDistObs" << " "; + vertex_stat_file << std::setw(width_values) << "DegDistEns" << " "; + vertex_stat_file << std::setw(width_values) << "DegDistEnsStd" << " "; + vertex_stat_file << std::setw(width_values) << "CDegDistEns" << " "; + vertex_stat_file << std::setw(width_values) << "CDegDistEnsStd" << " "; + // vertex_stat_file << std::setw(width_values) << "SumDegNObs" << " "; + vertex_stat_file << std::setw(width_values) << "SumDegNEns" << " "; + vertex_stat_file << std::setw(width_values) << "SumDegNEnsStd" << " "; + // vertex_stat_file << std::setw(width_values) << "AvgDegNObs" << " "; + vertex_stat_file << std::setw(width_values) << "AvgDegNEns" << " "; + vertex_stat_file << std::setw(width_values) << "AvgDegNEnsStd" << " "; + // vertex_stat_file << std::setw(width_values) << "NbTriangObs" << " "; + vertex_stat_file << std::setw(width_values) << "NbTriangEns" << " "; + vertex_stat_file << std::setw(width_values) << "NbTriangEnsStd" << " "; + // vertex_stat_file << std::setw(width_values) << "ClustObs" << " "; + vertex_stat_file << std::setw(width_values) << "ClustEns" << " "; + vertex_stat_file << std::setw(width_values) << "ClustEnsStd" << " "; + vertex_stat_file << std::endl; + // Writes the hidden variables. + it3 = characterizing_inferred_ensemble_vstat.begin(); + end3 = characterizing_inferred_ensemble_vstat.end(); + for(int v, d; it3!=end3; ++it3) + { + d = it3->first; + vertex_stat_file << std::setw(width_values) << d << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][0] << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][1] << " "; + vertex_stat_file << std::setw(width_values) << avg_comp_cumul_degree_dist[d] << " "; + vertex_stat_file << std::setw(width_values) << std_comp_cumul_degree_dist[d] << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][2] << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][3] << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][4] << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][5] << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][6] << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][7] << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][8] << " "; + vertex_stat_file << std::setw(width_values) << characterizing_inferred_ensemble_vstat[d][9] << " "; + vertex_stat_file << std::endl; + } + vertex_stat_file.close(); + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "=> Inferred ensemble statistics by degree class saved to " << ROOTNAME_OUTPUT + ".inf_vstat" << std::endl; } + + + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << "Extracting the original graph statistics by degree class..."; } + + + // Extracts the vertex statistics of the original edgelist by degree class. + std::map original_stat_degree; + std::map original_stat_sum_degree_neighbors; + std::map original_stat_avg_degree_neighbors; + std::map original_stat_nb_triangles; + std::map original_stat_clustering; + for(int v1(0), d1; v1 0) + { + original_stat_sum_degree_neighbors[d1] += sum_degree_of_neighbors[v1]; + original_stat_avg_degree_neighbors[d1] += sum_degree_of_neighbors[v1] / d1; + } + if(d1 > 1) + { + original_stat_nb_triangles[d1] += nbtriangles[v1]; + original_stat_clustering[d1] += 2 * nbtriangles[v1] / d1 / (d1 - 1); + } + } + if(!QUIET_MODE) { std::clog << "...........................done." << std::endl; } + + // Sets the name of the file to write the vertices properties into. + std::string graph_stat_filename = ROOTNAME_OUTPUT + ".obs_vstat"; + // Opens the stream and terminates if the operation did not succeed. + std::fstream graph_stat_file(graph_stat_filename.c_str(), std::fstream::out); + if( !graph_stat_file.is_open() ) + { + std::cerr << "Could not open file: " << graph_stat_filename << "." << std::endl; + std::terminate(); + } + + // Writes the header. + graph_stat_file << "#"; + graph_stat_file << std::setw(width_values - 1) << "Degree" << " "; + graph_stat_file << std::setw(width_values) << "DegDist" << " "; + graph_stat_file << std::setw(width_values) << "CDegDist" << " "; + graph_stat_file << std::setw(width_values) << "SumDegN" << " "; + graph_stat_file << std::setw(width_values) << "AvgDegN" << " "; + graph_stat_file << std::setw(width_values) << "NbTriang" << " "; + graph_stat_file << std::setw(width_values) << "Clust" << " "; + graph_stat_file << std::endl; + // Writes the hidden variables. + double ccdegdist = 1; + it2 = original_stat_degree.begin(); + end2 = original_stat_degree.end(); + for(int v, d, norm; it2!=end2; ++it2) + { + d = it2->first; + norm = it2->second; + graph_stat_file << std::setw(width_values) << d << " "; + graph_stat_file << std::setw(width_values) << original_stat_degree[d] / nb_vertices << " "; + graph_stat_file << std::setw(width_values) << ccdegdist << " "; + ccdegdist -= original_stat_degree[d] / nb_vertices; + graph_stat_file << std::setw(width_values) << original_stat_sum_degree_neighbors[d] / norm << " "; + graph_stat_file << std::setw(width_values) << original_stat_avg_degree_neighbors[d] / norm << " "; + graph_stat_file << std::setw(width_values) << original_stat_nb_triangles[d] / norm << " "; + graph_stat_file << std::setw(width_values) << original_stat_clustering[d] / norm << " "; + graph_stat_file << std::endl; + } + graph_stat_file.close(); + + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "=> Original graph statistics by degree class saved to " << ROOTNAME_OUTPUT + ".obs_vstat" << std::endl; } + // // Gets the current time. + // time2 = std::time(NULL); +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::save_inferred_theta_density() +{ + // Builds the bins. + std::map bins; + std::map::iterator it; + int cnt = 0; + int nb_bins = 25; + double dt = 2 * PI / nb_bins; + for(double t(dt), tt(2 * PI + 0.001); t n(bins.size(), 0); + // Computes the connection probability for every pair of vertices. + for(int v1(0), i; v1second; + n[i] += 1; + } + // Writes the connection probability into a file. + std::string theta_density_filename = ROOTNAME_OUTPUT + ".inf_theta_density"; + std::fstream theta_density_file(theta_density_filename.c_str(), std::fstream::out); + if( !theta_density_file.is_open() ) + { + std::cerr << "Could not open file: " << theta_density_filename << "." << std::endl; + std::terminate(); + } + theta_density_file << "#"; + theta_density_file << std::setw(width_values - 1) << "Theta" << " "; + theta_density_file << std::setw(width_values) << "InfDensity" << " "; + theta_density_file << std::setw(width_values) << "ThDensity" << " "; + theta_density_file << std::endl; + for(int i(0), ii(n.size()); i Inferred theta density saved to " << ROOTNAME_OUTPUT + ".inf_theta_density" << std::endl; } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +double embeddingS1_t::time_since_epoch_in_seconds() +{ + // double tmp = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + // return tmp / 1000; + // https://en.cppreference.com/w/cpp/chrono/c/clock + clock_t t = clock(); + return ((float)t) / (CLOCKS_PER_SEC); +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +int embeddingS1_t::get_root(int i, std::vector &clust_id) +{ + while(i != clust_id[i]) + { + clust_id[i] = clust_id[clust_id[i]]; + i = clust_id[i]; + } + return i; +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::merge_clusters(std::vector &size, std::vector &clust_id) +{ + // Variables. + int v1, v2, v3, v4; + // Iterators. + std::set::iterator it, end; + // Loops over the vertices. + for(int i(0); i size[v1]) + std::swap(v1, v2); + v3 = get_root(v1, clust_id); + v4 = get_root(v2, clust_id); + clust_id[v4] = v3; + size[v3] += size[v4]; + } + } + } +} + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void embeddingS1_t::check_connected_components() +{ + // Vector containing the ID of the component to which each node belongs. + std::vector Vertex2Prop(nb_vertices, -1); + + // Vector containing the size of the components. + std::vector connected_components_size; + + // Set ordering the component according to their size. + std::set< std::pair > ordered_connected_components; + + // Starts with every vertex as an isolated cluster. + std::vector clust_id(nb_vertices); + std::vector clust_size(nb_vertices, 1); + for(int v(0); v CompID; + for(int v(0); vsecond; + int lcc_size = (--ordered_connected_components.end())->first; + + if(lcc_size != nb_vertices) + { + if(!QUIET_MODE) { std::clog << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "- More than one component found!!" << std::endl; } + if(!QUIET_MODE) { std::clog << TAB << "- " << lcc_size << "/" << nb_vertices << " vertices in the largest component." << std::endl; } + std::cerr << std::endl; + std::cerr << "More than one component found (" << lcc_size << "/" << nb_vertices << ") vertices in the largest component." << std::endl; + + std::string edgelist_rootname; + size_t lastdot = EDGELIST_FILENAME.find_last_of("."); + if(lastdot == std::string::npos) + { + edgelist_rootname = EDGELIST_FILENAME; + } + edgelist_rootname = EDGELIST_FILENAME.substr(0, lastdot); + + // Sets the name of the file to write the hidden variables into. + std::string edgelist_filename = edgelist_rootname + "_GC.edge"; + // Opens the stream and terminates if the operation did not succeed. + std::fstream edgelist_file(edgelist_filename.c_str(), std::fstream::out); + if( !edgelist_file.is_open() ) + { + std::cerr << "Could not open file: " << edgelist_filename << "." << std::endl; + std::terminate(); + } + + std::set::iterator it, end; + for(int v1(0), v2, c1, c2; v1 degree[v2]) +// { +// std::swap(v1, v2); +// } +// // Computes the loglikelihood between vertives v1 and v2 (checks with the vertex with smaller degree). +// if(adjacency_list[v1].find(v2) == adjacency_list[v1].end()) // not neighbors +// { +// return -1 * std::log( 1 + std::pow( (nb_vertices * da) / (2 * PI * mu * kappa[v1] * kappa[v2]), -beta) ); +// } +// else // neighbors +// { +// return -1 * std::log( 1 + std::pow( (nb_vertices * da) / (2 * PI * mu * kappa[v1] * kappa[v2]), beta) ); +// } +// } + + +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// double embeddingS1_t::find_optimal_angle(int v1) +// { +// // Variables. +// double tmp_angle; +// double tmp_loglikelihood; +// double previous_angle = theta[v1]; +// double best_angle = previous_angle; +// // double max_step_length = 2 * PI / MINIMAL_ANGULAR_RESOLUTION; +// double max_step_length = 2 * 2 * PI / nb_vertices; +// // Computes the current loglikelihood. +// double previous_loglikelihood = 0; +// for(int v2(0); v2 (2 * PI)) ? (t1 - (2 * PI)) : t1; +// // Computes the local loglikelihood. +// tmp_loglikelihood = 0; +// for(int v2(0); v2 best_loglikelihood) +// { +// best_loglikelihood = tmp_loglikelihood; +// best_angle = tmp_angle; +// } +// // Increases the angle. +// t1 += max_step_length * uniform_01(engine); +// } +// // // Refines the position around the best position found above. +// // double close_angular_range = CLOSE_ANGULAR_RANGE_FACTOR * 2 * PI / nb_vertices; +// // double refined_max_step_length = 2 * PI / nb_vertices / REFINED_MAX_STEP_LENGTH_DIVISOR; +// // t0 = best_angle - close_angular_range ; +// // t0 = (t0 < 0) ? (t0 + (2 * PI)) : t0; +// // t1 = t0; +// // while( (t1 - t0) < (2 * close_angular_range) ) +// // { +// // // Gets the angle in the standard range. +// // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; +// // // Computes the local loglikelihood. +// // tmp_loglikelihood = 0; +// // for(int v2(0); v2 best_loglikelihood) +// // { +// // best_loglikelihood = tmp_loglikelihood; +// // best_angle = tmp_angle; +// // } +// // // Increases the angle. +// // t1 += refined_max_step_length * uniform_01(engine); +// // } +// // Registers the best position found. +// theta[v1] = best_angle; +// // Returns the variation in the global loglikelihood. +// // return (best_loglikelihood - previous_loglikelihood) / nb_edges; +// return PI - std::fabs( PI - std::fabs(best_angle - previous_angle) ); +// } + + +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// void embeddingS1_t::refine_angle5(int v1) +// { +// // Variables. +// double tmp_angle; +// double tmp_loglikelihood; +// double best_angle = theta[v1]; +// // Computes the current loglikelihood. +// double previous_loglikelihood = 0; +// for(int v2(0); v2::iterator it2 = adjacency_list[v1].begin(); +// std::set::iterator end = adjacency_list[v1].end(); +// double t2, k2, da; +// double sum_sin_theta = 0; +// double sum_cos_theta = 0; +// for(; it2!=end; ++it2) +// { +// // Identifies the neighbor. +// t2 = theta[*it2]; +// k2 = kappa[*it2]; + +// // Computes the average angle of neighbors. +// sum_sin_theta += std::sin(t2) / (k2 * k2); +// sum_cos_theta += std::cos(t2) / (k2 * k2); +// } +// double average_theta = std::atan2(sum_sin_theta, sum_cos_theta); +// while(average_theta > (2 * PI)) +// average_theta = average_theta - (2 * PI); +// while(average_theta < 0) +// average_theta = average_theta + (2 * PI); + +// // Finds the largest angular distance between the neighbor and the average position. +// // double max_angle = PI / 4; +// double max_angle = PI / 6; +// it2 = adjacency_list[v1].begin(); +// for(; it2!=end; ++it2) +// { +// da = PI - std::fabs(PI - std::fabs(average_theta - theta[*it2])); +// if(da > max_angle) +// { +// max_angle = da; +// } +// } +// max_angle /= 2; + +// // std::clog << average_theta << " " << max_angle << std::endl; +// // std::clog.flush(); + +// // Considers various wisely chosen new angular positions and keeps the best. +// int _nb_new_angles_to_try = NB_NEW_ANGLES_TO_TRY * std::log(nb_vertices); +// // int _nb_new_angles_to_try = max_angle * nb_vertices / PI; +// // if(_nb_new_angles_to_try < NB_NEW_ANGLES_TO_TRY) +// // _nb_new_angles_to_try = NB_NEW_ANGLES_TO_TRY; +// for(int e(0); e<_nb_new_angles_to_try; ++e) +// { +// // Gets the angle in the standard range. +// tmp_angle = (normal_01(engine) * max_angle) + average_theta; +// while(tmp_angle > (2 * PI)) +// tmp_angle = tmp_angle - (2 * PI); +// while(tmp_angle < 0) +// tmp_angle = tmp_angle + (2 * PI); + +// // Computes the local loglikelihood. +// tmp_loglikelihood = 0; +// for(int v2(0); v2 best_loglikelihood) +// { +// best_loglikelihood = tmp_loglikelihood; +// best_angle = tmp_angle; +// } +// } + +// // Registers the best position found. +// theta[v1] = best_angle; +// } + + +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// void embeddingS1_t::refine_angle3(int v1) +// { +// // Variables. +// double tmp_angle; +// double tmp_loglikelihood; +// double previous_angle = theta[v1]; +// double best_angle = previous_angle; +// // double max_step_length = 2 * PI / MINIMAL_ANGULAR_RESOLUTION; +// double max_step_length = 2 * 2 * PI / nb_vertices; +// // Computes the current loglikelihood. +// double previous_loglikelihood = 0; +// for(int v2(0); v2 (2 * PI)) ? (t1 - (2 * PI)) : t1; +// tmp_angle = t1; +// while(tmp_angle > (2 * PI)) +// tmp_angle = tmp_angle - (2 * PI); +// while(tmp_angle < 0) +// tmp_angle = tmp_angle + (2 * PI); +// // Computes the local loglikelihood. +// tmp_loglikelihood = 0; +// for(int v2(0); v2 best_loglikelihood) +// { +// best_loglikelihood = tmp_loglikelihood; +// best_angle = tmp_angle; +// } +// // Increases the angle. +// t1 += max_step_length * uniform_01(engine); +// } +// // // Refines the position around the best position found above. +// // // double close_angular_range = CLOSE_ANGULAR_RANGE_FACTOR * 2 * PI / nb_vertices; +// // // double refined_max_step_length = 2 * PI / nb_vertices / REFINED_MAX_STEP_LENGTH_DIVISOR; +// // // t0 = best_angle - close_angular_range ; +// // t0 = best_angle_prov - (200 * max_step_length) ; +// // // t0 = (t0 < 0) ? (t0 + (2 * PI)) : t0; +// // while(t0 > (2 * PI)) +// // t0 = t0 - (2 * PI); +// // while(t0 < 0) +// // t0 = t0 + (2 * PI); +// // t1 = t0; +// // while( (t1 - t0) < (2 * 200 * max_step_length) ) +// // { +// // // Gets the angle in the standard range. +// // // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; +// // tmp_angle = t1; +// // while(tmp_angle > (2 * PI)) +// // tmp_angle = tmp_angle - (2 * PI); +// // while(tmp_angle < 0) +// // tmp_angle = tmp_angle + (2 * PI); +// // // Computes the local loglikelihood. +// // tmp_loglikelihood = 0; +// // for(int v2(0); v2 best_loglikelihood) +// // { +// // best_loglikelihood = tmp_loglikelihood; +// // best_angle = tmp_angle; +// // } +// // // Increases the angle. +// // t1 += max_step_length * uniform_01(engine); +// // } +// // Registers the best position found. +// theta[v1] = best_angle; +// // Returns the variation in the global loglikelihood. +// // return (best_loglikelihood - previous_loglikelihood) / nb_edges; +// // return PI - std::fabs( PI - std::fabs(best_angle - previous_angle) ); +// } + + +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// void embeddingS1_t::refine_angle4(int v1) +// { +// // Variables. +// double tmp_angle; +// double best_angle_prov; +// double tmp_loglikelihood; +// double tmp_partial_loglikelihood; +// double previous_angle = theta[v1]; +// double best_angle = previous_angle; +// // double max_step_length = 2 * PI / MINIMAL_ANGULAR_RESOLUTION; +// double max_step_length = 2 * 2 * PI / nb_vertices; +// // Computes the current loglikelihood. +// double previous_loglikelihood = 0; +// for(int v2(0); v2::iterator it2; +// std::set::iterator end; +// double best_partial_loglikelihood = -1e100; +// double t0 = 2 * PI * uniform_01(engine); +// double t1 = t0; +// while( (t1 - t0) < (2 * PI) ) +// { +// // Gets the angle in the standard range. +// // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; +// tmp_angle = t1; +// while(tmp_angle >= (2 * PI)) +// tmp_angle = tmp_angle - (2 * PI); +// // while(tmp_angle < 0) +// // tmp_angle = tmp_angle + (2 * PI); +// // Computes the local loglikelihood. +// tmp_partial_loglikelihood = 0; +// it2 = adjacency_list[v1].begin(); +// end = adjacency_list[v1].end(); +// for(; it2!=end; ++it2) +// { +// tmp_partial_loglikelihood += compute_pairwise_loglikelihood(v1, tmp_angle, *it2, theta[*it2]); +// } +// // std::cout << tmp_partial_loglikelihood << std::endl; +// // Preserves the optimal angular sector. +// if(tmp_partial_loglikelihood > best_partial_loglikelihood) +// { +// best_partial_loglikelihood = tmp_partial_loglikelihood; +// best_angle_prov = tmp_angle; +// } +// // Increases the angle. +// t1 += max_step_length * uniform_01(engine); +// } +// // Refines the position around the best position found above. +// // double close_angular_range = CLOSE_ANGULAR_RANGE_FACTOR * 2 * PI / nb_vertices; +// // double refined_max_step_length = 2 * PI / nb_vertices / REFINED_MAX_STEP_LENGTH_DIVISOR; +// // t0 = best_angle - close_angular_range ; +// // t0 = best_angle_prov - (250 * max_step_length) ; +// t0 = best_angle_prov - (PI / 12) ; +// // t0 = (t0 < 0) ? (t0 + (2 * PI)) : t0; +// // while(t0 > (2 * PI)) +// // t0 = t0 - (2 * PI); +// while(t0 < 0) +// t0 = t0 + (2 * PI); +// t1 = t0; +// // while( (t1 - t0) < (2 * 250 * max_step_length) ) +// while( (t1 - t0) < (2 * PI / 12) ) +// { +// // Gets the angle in the standard range. +// // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; +// tmp_angle = t1; +// while(tmp_angle >= (2 * PI)) +// tmp_angle = tmp_angle - (2 * PI); +// // while(tmp_angle < 0) +// // tmp_angle = tmp_angle + (2 * PI); +// // Computes the local loglikelihood. +// tmp_loglikelihood = 0; +// for(int v2(0); v2 best_loglikelihood) +// { +// best_loglikelihood = tmp_loglikelihood; +// best_angle = tmp_angle; +// } +// // Increases the angle. +// t1 += max_step_length * uniform_01(engine); +// } +// // Registers the best position found. +// theta[v1] = best_angle; +// // Returns the variation in the global loglikelihood. +// // return (best_loglikelihood - previous_loglikelihood) / nb_edges; +// // return PI - std::fabs( PI - std::fabs(best_angle - previous_angle) ); +// } + + +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// void embeddingS1_t::refine_angle2(int v1) +// { +// // Variables. +// double tmp_angle; +// double tmp_loglikelihood; +// double best_angle = theta[v1]; +// // Computes the current loglikelihood. +// double previous_loglikelihood = 0; +// for(int v2(0); v2::iterator it2 = adjacency_list[v1].begin(); +// std::set::iterator end = adjacency_list[v1].end(); +// double t2, k2, da; +// double sum_sin_theta = 0; +// double sum_cos_theta = 0; +// for(; it2!=end; ++it2) +// { +// // Identifies the neighbor. +// t2 = theta[*it2]; +// k2 = kappa[*it2]; + +// // Computes the average angle of neighbors. +// sum_sin_theta += std::sin(t2) / (k2 * k2); +// sum_cos_theta += std::cos(t2) / (k2 * k2); +// } +// double average_theta = std::atan2(sum_sin_theta, sum_cos_theta); +// while(average_theta > (2 * PI)) +// average_theta = average_theta - (2 * PI); +// while(average_theta < 0) +// average_theta = average_theta + (2 * PI); + +// // Finds the largest angular distance between the neighbor and the average position. +// double max_angle = PI / 6; +// // double max_angle = PI / 12; +// it2 = adjacency_list[v1].begin(); +// for(; it2!=end; ++it2) +// { +// da = PI - std::fabs(PI - std::fabs(average_theta - theta[*it2])); +// if(da > max_angle) +// { +// max_angle = da; +// } +// } +// max_angle /= 2; + +// // std::clog << average_theta << " " << max_angle << std::endl; +// // std::clog.flush(); + +// // Considers various wisely chosen new angular positions and keeps the best. +// int _nb_new_angles_to_try = max_angle * nb_vertices / PI; +// if(_nb_new_angles_to_try < NB_NEW_ANGLES_TO_TRY) +// _nb_new_angles_to_try = NB_NEW_ANGLES_TO_TRY; +// for(int e(0); e<_nb_new_angles_to_try; ++e) +// { +// // Gets the angle in the standard range. +// tmp_angle = (normal_01(engine) * max_angle) + average_theta; +// while(tmp_angle > (2 * PI)) +// tmp_angle = tmp_angle - (2 * PI); +// while(tmp_angle < 0) +// tmp_angle = tmp_angle + (2 * PI); + +// // Computes the local loglikelihood. +// tmp_loglikelihood = 0; +// for(int v2(0); v2 best_loglikelihood) +// { +// best_loglikelihood = tmp_loglikelihood; +// best_angle = tmp_angle; +// } +// } + +// // Registers the best position found. +// theta[v1] = best_angle; +// } + + +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// void embeddingS1_t::infer_optimal_positions() +// { +// // Sets the convergence threshold. +// double maximization_convergence_threshold = MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD; +// // if(nb_vertices < LIMIT_FOR_CONVERGENCE_CRITERION) +// // { +// // maximization_convergence_threshold = MINIMAL_ANGULAR_CONVERGENCE_THRESHOLD; +// // // maximization_convergence_threshold = 2 * PI / nb_vertices; +// // } +// // else +// // { +// // maximization_convergence_threshold = 2 * PI / std::sqrt(LIMIT_FOR_CONVERGENCE_CRITERION * nb_vertices); +// // } +// if(!QUIET_MODE) { std::clog << "Maximizing positions..."; } +// if(!QUIET_MODE) { std::clog << std::endl; } +// if(!QUIET_MODE) { std::clog << TAB << "Convergence threshold: " << maximization_convergence_threshold << std::endl; } +// if(!QUIET_MODE) { std::clog << TAB << " "; } +// if(!QUIET_MODE) { std::clog << "Average angular displacement by centrality"; } +// if(!QUIET_MODE) { std::clog << TAB << " "; } +// if(!QUIET_MODE) { std::clog << "Distribution of the angular displacement"; } +// if(!QUIET_MODE) { std::clog << std::endl; } +// if(!QUIET_MODE) { std::clog << TAB; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "iter." << " "; } +// if(!QUIET_MODE) { std::clog << TAB; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[1,20)" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[20,40)" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[40,60)" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[60,80)" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "[80,100]" << " "; } +// if(!QUIET_MODE) { std::clog << TAB; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "min" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "5th" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "25th" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "median" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "average" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "75th" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "95th" << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << "max" << " "; } +// if(!QUIET_MODE) { std::clog << TAB; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(12) << "time (sec.)" << " "; } +// if(!QUIET_MODE) { std::clog << std::endl; } +// // Variables. +// time_t start_time, stop_time; +// double avg_diff_angle; +// double avg_diff_angle_per_sector; +// int nb_iterations = 1; +// int index_05 = (0.05 * nb_vertices) - 1; +// int index_25 = (0.25 * nb_vertices) - 1; +// int index_50 = (0.50 * nb_vertices) - 1; +// int index_75 = (0.75 * nb_vertices) - 1; +// int index_95 = (0.95 * nb_vertices) - 1; +// std::vector angular_displacement(nb_vertices, 10); +// bool keep_going = true; +// while( keep_going ) +// { +// // // Looks for inversions and correct them. +// // correct_inversions(); +// start_time = std::time(NULL); +// if(!QUIET_MODE) { std::clog << TAB; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << nb_iterations << " "; } +// if(!QUIET_MODE) { std::clog.flush(); } +// // Finds the best angle taking into account the position of other vertices. +// avg_diff_angle = 0; +// for(int i(0); (i nb_vertices) ? (nb_vertices) : i_end; +// avg_diff_angle_per_sector = 0; +// for(int j(i_begin); j MAXIMIZATION_CONVERGENCE_THRESHOLD) +// if(avg_diff_angle_per_sector > maximization_convergence_threshold) +// { +// keep_going = true; +// } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << avg_diff_angle_per_sector << " "; } +// } +// // Orders the angular displacements. +// std::sort(angular_displacement.begin(), angular_displacement.end()); +// // Characterizes the dispersion. +// if(!QUIET_MODE) { std::clog << TAB; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[0] << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_05] << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_25] << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_50] << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << avg_diff_angle << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_75] << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[index_95] << " "; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(8) << angular_displacement[nb_vertices - 1] << " "; } +// // Time elapsed. +// stop_time = std::time(NULL); +// if(!QUIET_MODE) { std::clog << TAB; } +// if(!QUIET_MODE) { std::clog << std::fixed << std::setw(12) << stop_time - start_time << " "; } +// if(!QUIET_MODE) { std::clog << std::endl; } +// // Updates the number of iterations. +// ++nb_iterations; +// if(nb_iterations == MAX_NB_ITER_MAXIMIZATION) +// { +// if(!QUIET_MODE) { std::clog << "WARNING: maximum number of iterations reached" << std::endl; } +// break; +// } +// } +// if(!QUIET_MODE) { std::clog << " ...............................................................done." << std::endl; } +// if(!QUIET_MODE) { std::clog << std::endl; } +// } + + +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// // =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// double embeddingS1_t::find_approximate_optimal_angle(int v1) +// { +// // Variables. +// double tmp_angle; +// double tmp_loglikelihood; +// double previous_angle = theta[v1]; +// double best_angle = previous_angle; +// // double max_step_length = 2 * PI / MINIMAL_ANGULAR_RESOLUTION; +// double max_step_length = 2 * 2 * PI / nb_vertices; +// // Iterators. +// std::set::iterator it, end; +// // Computes the current loglikelihood. +// double previous_loglikelihood = 0; +// it = adjacency_list[v1].begin(); +// end = adjacency_list[v1].end(); +// for(int v2; it!=end; ++it) +// { +// v2 = *it; +// previous_loglikelihood += compute_pairwise_loglikelihood(v1, best_angle, v2, theta[v2]); +// } +// double best_loglikelihood = previous_loglikelihood; +// // For "all" angles, starting at a random position. +// double t0 = 2 * PI * uniform_01(engine); +// double t1 = t0; +// while( (t1 - t0) < (2 * PI) ) +// { +// // Gets the angle in the standard range. +// tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; +// // Computes the local loglikelihood. +// tmp_loglikelihood = 0; +// it = adjacency_list[v1].begin(); +// end = adjacency_list[v1].end(); +// for(int v2; it!=end; ++it) +// { +// v2 = *it; +// tmp_loglikelihood += compute_pairwise_loglikelihood(v1, tmp_angle, v2, theta[v2]); +// } +// // Preserves the optimal angular sector. +// if(tmp_loglikelihood > best_loglikelihood) +// { +// best_loglikelihood = tmp_loglikelihood; +// best_angle = tmp_angle; +// } +// // Increases the angle. +// t1 += max_step_length * uniform_01(engine); +// } +// // // Refines the position around the best position found above. +// // double close_angular_range = CLOSE_ANGULAR_RANGE_FACTOR * 2 * PI / nb_vertices; +// // double refined_max_step_length = 2 * PI / nb_vertices / REFINED_MAX_STEP_LENGTH_DIVISOR; +// // t0 = best_angle - close_angular_range ; +// // t0 = (t0 < 0) ? (t0 + (2 * PI)) : t0; +// // t1 = t0; +// // while( (t1 - t0) < (2 * close_angular_range) ) +// // { +// // // Gets the angle in the standard range. +// // tmp_angle = (t1 > (2 * PI)) ? (t1 - (2 * PI)) : t1; +// // // Computes the local loglikelihood. +// // tmp_loglikelihood = 0; +// // it = adjacency_list[v1].begin(); +// // end = adjacency_list[v1].end(); +// // for(int v2; it!=end; ++it) +// // { +// // v2 = *it; +// // tmp_loglikelihood += compute_pairwise_loglikelihood(v1, tmp_angle, v2, theta[v2]); +// // } +// // // Preserves the optimal angular sector. +// // if(tmp_loglikelihood > best_loglikelihood) +// // { +// // best_loglikelihood = tmp_loglikelihood; +// // best_angle = tmp_angle; +// // } +// // // Increases the angle. +// // t1 += refined_max_step_length * uniform_01(engine); +// // } +// // Registers the best position found. +// theta[v1] = best_angle; +// // Returns the variation in the global loglikelihood. +// // return (best_loglikelihood - previous_loglikelihood) / nb_edges; +// return PI - std::fabs( PI - std::fabs(best_angle - previous_angle) ); +// } diff --git a/include/embeddingS1_unix.hpp b/include/embeddingS1_unix.hpp index 2829ef7..ae60c1f 100644 --- a/include/embeddingS1_unix.hpp +++ b/include/embeddingS1_unix.hpp @@ -1,192 +1,206 @@ -/* - * - * Provides the functions related to UNIX operating system. - * - * Author: Antoine Allard - * WWW: antoineallard.info - * Date: September 2017 - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - */ - -#ifndef EMBEDDINGS1_UNIX_HPP_INCLUDED -#define EMBEDDINGS1_UNIX_HPP_INCLUDED - -// Standard Template Library -#include -#include -#include -// Operating System -#include -// embeddingS1 -#include "embeddingS1.hpp" - - - - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// Prints the information on the way the command line UNIX program should be used. -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void print_usage() -{ - std::cout << std::endl; - std::cout << "NAME" << std::endl; - std::cout << "\tMercator: Inference of high-quality embeddings of complex networks into the" << std::endl; - std::cout << "\t hyperbolic disk" << std::endl; - std::cout << std::endl; - std::cout << "SYNOPSIS" << std::endl; - std::cout << "\tmercator [options] " << std::endl; - std::cout << std::endl; - std::cout << "INPUT" << std::endl; - std::cout << "\tThe structure of the graph is provided by a text file containing it edgelist. Each" << std::endl; - std::cout << "\tline in the file corresponds to an edge in the graph (i.e., [VERTEX1] [VERTEX2])." << std::endl; - std::cout << "\t - The name of the vertices need not be integers (they are stored as std::string)." << std::endl; - std::cout << "\t - Directed graphs will be converted to undirected." << std::endl; - std::cout << "\t - Multiple edges, self-loops and weights will be ignored." << std::endl; - std::cout << "\t - Lines starting with '# ' are ignored (i.e., comments)." << std::endl; - // std::cout << std::endl; - // std::cout << "\tOuput: a file containing the inferred coordinates (kappa, theta) for each vertex." << std::endl; - std::cout << std::endl; -} - - - - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// Prints the information about the options of the command line UNIX program should be used. -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void print_help() -{ - std::cout << std::endl; - std::cout << "The following options are available:" << std::endl; - std::cout << "\t-a Screen mode. Program outputs details about its progress on screen" << std::endl; - std::cout << "\t (through std::clog) instead of in a log file. Useful to gather all" << std::endl; - std::cout << "\t output in a single file if mercator is a subroutine of a script." << std::endl; - std::cout << "\t-b [VALUE] Specify the value for beta to be used for the embedding. By " << std::endl; - std::cout << "\t default the program infers the value of beta based on the average" << std::endl; - std::cout << "\t local clustering coefficient of the original edgelist." << std::endl; - std::cout << "\t-c Clean mode. Writes the inferred coordinates in clean file without" << std::endl; - std::cout << "\t any lines starting by # to facilitate their use in custom computer" << std::endl; - std::cout << "\t programs." << std::endl; - std::cout << "\t-f Fast mode. Does not infer the positions based on likelihood" << std::endl; - std::cout << "\t maximization, rather uses only the EigenMap method." << std::endl; - std::cout << "\t-k No post-processing of the values of kappa based on the inferred" << std::endl; - std::cout << "\t angular positions (theta) resulting in every vertices with the same" << std::endl; - std::cout << "\t degree ending at the same radial position in the hyperbolic disk." << std::endl; - // std::cout << "\t-h Print this message on screen and exit." << std::endl; - std::cout << "\t-o [ROOTNAME] Specify the rootname used for all output files. Default: uses the" << std::endl; - std::cout << "\t rootname of the edgelist file as (i.e., rootname.edge)." << std::endl; - std::cout << "\t-r [FILENAME] Refine mode. Reads the inferred positions from a previous run of" << std::endl; - std::cout << "\t this program (file *.inf_coord) and refines the inferred positions." << std::endl; - std::cout << "\t-q Quiet mode. Program does not output information about the network" << std::endl; - std::cout << "\t and the embedding procedure." << std::endl; - std::cout << "\t-s [SEED] Program uses a custom seed for the random number generator." << std::endl; - std::cout << "\t Default: EPOCH." << std::endl; - std::cout << "\t-v Validation mode. Validates and characterizes the inferred random" << std::endl; - std::cout << "\t network ensemble." << std::endl; - std::cout << std::endl; - // std::cout << "Please see file embeddingS1.hpp for more options (will require recompilation)." << std::endl; - // std::cout << std::endl; -} - - - - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// Parses the options (for UNIX-like command line use) and returns the filename of the edgelist. -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void parse_options(int argc , char *argv[], embeddingS1_t &the_graph) -{ - // Shows the options if no argument is given. - if(argc == 1) - { - print_usage(); - print_help(); - std::exit(0); - } - - // - the_graph.EDGELIST_FILENAME = argv[argc - 1]; - - // Parsing options. - int opt; - while ((opt = getopt(argc,argv,"ab:cfko:r:qs:v")) != -1) - { - switch(opt) - { - case 'a': - the_graph.VERBOSE_MODE = true; - break; - - case 'b': - the_graph.CUSTOM_BETA = true; - the_graph.beta = std::stod(optarg); - break; - - case 'c': - the_graph.CLEAN_RAW_OUTPUT_MODE = true; - break; - - case 'f': - the_graph.MAXIMIZATION_MODE = false; - break; - - // case 'h': - // print_usage(); - // print_help(); - // std::exit(0); - - case 'k': - the_graph.KAPPA_POST_INFERENCE_MODE = false; - break; - - case 'o': - the_graph.CUSTOM_OUTPUT_ROOTNAME_MODE = true; - the_graph.ROOTNAME_OUTPUT = optarg; - break; - - case 'r': - the_graph.REFINE_MODE = true; - the_graph.ALREADY_INFERRED_PARAMETERS_FILENAME = optarg; - break; - - case 'q': - the_graph.QUIET_MODE = true; - break; - - case 's': - the_graph.CUSTOM_SEED = true; - the_graph.SEED = std::stoi(optarg); - break; - - case 'v': - the_graph.VALIDATION_MODE = true; - the_graph.CHARACTERIZATION_MODE = true; - break; - - default: - print_usage(); - print_help(); - std::exit(0); - } - } -} - -#endif // EMBEDDINGS1_UNIX_HPP_INCLUDED +/* + * + * Provides the functions related to UNIX operating system. + * + * Author: Antoine Allard + * WWW: antoineallard.info + * Date: September 2017 + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + */ + +#ifndef EMBEDDINGS1_UNIX_HPP_INCLUDED +#define EMBEDDINGS1_UNIX_HPP_INCLUDED + +// Standard Template Library +#include +#include +#include +// Operating System +#include +// embeddingS1 +#include "embeddingS1.hpp" + + + + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// Prints the information on the way the command line UNIX program should be used. +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void print_usage() +{ + std::cout << std::endl; + std::cout << "NAME" << std::endl; + std::cout << "\tMercator: Inference of high-quality embeddings of complex networks into the" << std::endl; + std::cout << "\t hyperbolic disk" << std::endl; + std::cout << std::endl; + std::cout << "SYNOPSIS" << std::endl; + std::cout << "\tmercator [options] " << std::endl; + std::cout << std::endl; + std::cout << "INPUT" << std::endl; + std::cout << "\tThe structure of the graph is provided by a text file containing it edgelist. Each" << std::endl; + std::cout << "\tline in the file corresponds to an edge in the graph (i.e., [VERTEX1] [VERTEX2])." << std::endl; + std::cout << "\t - The name of the vertices need not be integers (they are stored as std::string)." << std::endl; + std::cout << "\t - Directed graphs will be converted to undirected." << std::endl; + std::cout << "\t - Multiple edges, self-loops and weights will be ignored." << std::endl; + std::cout << "\t - Lines starting with '# ' are ignored (i.e., comments)." << std::endl; + // std::cout << std::endl; + // std::cout << "\tOuput: a file containing the inferred coordinates (kappa, theta) for each vertex." << std::endl; + std::cout << std::endl; +} + + + + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// Prints the information about the options of the command line UNIX program should be used. +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void print_help() +{ + std::cout << std::endl; + std::cout << "The following options are available:" << std::endl; + std::cout << "\t-a Screen mode. Program outputs details about its progress on screen" << std::endl; + std::cout << "\t (through std::clog) instead of in a log file. Useful to gather all" << std::endl; + std::cout << "\t output in a single file if mercator is a subroutine of a script." << std::endl; + std::cout << "\t-b [VALUE] Specify the value for beta to be used for the embedding. By " << std::endl; + std::cout << "\t default the program infers the value of beta based on the average" << std::endl; + std::cout << "\t local clustering coefficient of the original edgelist." << std::endl; + std::cout << "\t-c Clean mode. Writes the inferred coordinates in clean file without" << std::endl; + std::cout << "\t any lines starting by # to facilitate their use in custom computer" << std::endl; + std::cout << "\t programs." << std::endl; + std::cout << "\t-f Fast mode. Does not infer the positions based on likelihood" << std::endl; + std::cout << "\t maximization, rather uses only the EigenMap method." << std::endl; + std::cout << "\t-g Forces the condition beta > 1, the so called geometric phase," << std::endl; + std::cout << "\t ignoring the quasi- and non-geometric phases at beta < 1." << std::endl; + std::cout << "\t The default does take these regions into account." << std::endl; + std::cout << "\t-k No post-processing of the values of kappa based on the inferred" << std::endl; + std::cout << "\t angular positions (theta) resulting in every vertices with the same" << std::endl; + std::cout << "\t degree ending at the same radial position in the hyperbolic disk." << std::endl; + // std::cout << "\t-h Print this message on screen and exit." << std::endl; + std::cout << "\t-m Uses an analytic approximation for mu that holds for N >> 1." << std::endl; + std::cout << "\t The default calculates mu numerically, taking finite size effects " << std::endl; + std::cout << "\t into account." << std::endl; + std::cout << "\t-o [ROOTNAME] Specify the rootname used for all output files. Default: uses the" << std::endl; + std::cout << "\t rootname of the edgelist file as (i.e., rootname.edge)." << std::endl; + std::cout << "\t-r [FILENAME] Refine mode. Reads the inferred positions from a previous run of" << std::endl; + std::cout << "\t this program (file *.inf_coord) and refines the inferred positions." << std::endl; + std::cout << "\t-q Quiet mode. Program does not output information about the network" << std::endl; + std::cout << "\t and the embedding procedure." << std::endl; + std::cout << "\t-s [SEED] Program uses a custom seed for the random number generator." << std::endl; + std::cout << "\t Default: EPOCH." << std::endl; + std::cout << "\t-v Validation mode. Validates and characterizes the inferred random" << std::endl; + std::cout << "\t network ensemble." << std::endl; + std::cout << std::endl; + // std::cout << "Please see file embeddingS1.hpp for more options (will require recompilation)." << std::endl; + // std::cout << std::endl; +} + + + + + +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +// Parses the options (for UNIX-like command line use) and returns the filename of the edgelist. +// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= +void parse_options(int argc , char *argv[], embeddingS1_t &the_graph) +{ + // Shows the options if no argument is given. + if(argc == 1) + { + print_usage(); + print_help(); + std::exit(0); + } + + // + the_graph.EDGELIST_FILENAME = argv[argc - 1]; + + // Parsing options. + int opt; + while ((opt = getopt(argc,argv,"ab:gmcfko:r:qs:v")) != -1) + { + switch(opt) + { + case 'a': + the_graph.VERBOSE_MODE = true; + break; + + case 'b': + the_graph.CUSTOM_BETA = true; + the_graph.beta = std::stod(optarg); + break; + + case 'c': + the_graph.CLEAN_RAW_OUTPUT_MODE = true; + break; + + case 'f': + the_graph.MAXIMIZATION_MODE = false; + break; + + // case 'h': + // print_usage(); + // print_help(); + // std::exit(0); + + case 'g': + the_graph.ALL_BETA_MODE = false; + break; + + case 'm': + the_graph.NUMERIC_MU_MODE = false; + break; + + case 'k': + the_graph.KAPPA_POST_INFERENCE_MODE = false; + break; + + case 'o': + the_graph.CUSTOM_OUTPUT_ROOTNAME_MODE = true; + the_graph.ROOTNAME_OUTPUT = optarg; + break; + + case 'r': + the_graph.REFINE_MODE = true; + the_graph.ALREADY_INFERRED_PARAMETERS_FILENAME = optarg; + break; + + case 'q': + the_graph.QUIET_MODE = true; + break; + + case 's': + the_graph.CUSTOM_SEED = true; + the_graph.SEED = std::stoi(optarg); + break; + + case 'v': + the_graph.VALIDATION_MODE = true; + the_graph.CHARACTERIZATION_MODE = true; + break; + + default: + print_usage(); + print_help(); + std::exit(0); + } + } +} + +#endif // EMBEDDINGS1_UNIX_HPP_INCLUDED diff --git a/include/generatingS1.hpp b/include/generatingS1.hpp index c959483..954fe67 100644 --- a/include/generatingS1.hpp +++ b/include/generatingS1.hpp @@ -1,372 +1,374 @@ -/* - * - * This class provides the functions to generate a graph in the S1 space. - * - * Compilation requires the c++11 standard to use #include . - * Example: g++ -O3 -std=c++11 my_code.cpp -o my_program - * - * Author: Antoine Allard - * WWW: antoineallard.info - * Date: November 2017 - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - */ - -#ifndef GENERATINGS1_HPP_INCLUDED -#define GENERATINGS1_HPP_INCLUDED - -// Standard Template Library -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -class generatingS1_t -{ - // Flags controlling options. - public: - bool CUSTOM_OUTPUT_ROOTNAME_MODE = false; - bool NAME_PROVIDED = false; - bool NATIVE_INPUT_FILE = false; - bool THETA_PROVIDED = false; - bool OUTPUT_VERTICES_PROPERTIES = false; - // Global parameters. - public: - // Random number generator seed. - int SEED = std::time(NULL); - // Parameter beta (clustering). - double BETA = -1; - // Parameter mu (average degree). - double MU = -1; - // Rootname for the output files; - std::string OUTPUT_ROOTNAME = "default_output_rootname"; - // Input hidden variables filename. - std::string HIDDEN_VARIABLES_FILENAME; - // General internal objects. - private: - // pi - const double PI = 3.141592653589793238462643383279502884197; - // Random number generator - std::mt19937 engine; - std::uniform_real_distribution uniform_01; - // Mapping the numerical ID of vertices to their name. - std::vector Num2Name; - // Objects related to the graph ensemble. - private: - // Number of vertices. - int nb_vertices; - // Hidden variables of the vertices. - std::vector kappa; - // Positions of the vertices. - std::vector theta; - // Public functions to generate the graphs. - public: - // Constructor (empty). - generatingS1_t() {}; - // Loads the values of the hidden variables (i.e., kappa and theta). - void load_hidden_variables(); - // Generates an edgelist and writes it into a file. - void generate_edgelist(int width = 15); - // Private functions linked to the generation of a random edgelist. - private: - // Saves the values of the hidden variables (i.e., kappa and theta). - void save_vertices_properties(std::vector& rdegree, std::vector& edegree, int width); - // Gets and format current date/time. - std::string get_time(); -}; - - - - - -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -// =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= -void generatingS1_t::generate_edgelist(int width) -{ - // Initializes the random number generator. - engine.seed(SEED); - // Sets the name of the file to write the edgelist into. - std::string edgelist_filename = OUTPUT_ROOTNAME + ".edge"; - // Vectors containing the expected and real degrees. - std::vector edegree; - std::vector rdegree; - // Initializes the containers for the expected and real degrees. - if(OUTPUT_VERTICES_PROPERTIES) - { - edegree.resize(nb_vertices, 0); - rdegree.resize(nb_vertices, 0); - } - // Makes sure the value of beta has been provided. - if(BETA < 0) - { - std::cerr << "ERROR: The value of parameter beta must be provided." << std::endl; - std::terminate(); - } - // Sets the value of mu, if not provided. - if(MU < 0) - { - // Computes the average value of kappa. - double average_kappa = 0; - for(int v(0); v